feat(ddl): 为 DDL 视图增加按方言格式化展示能力

- 新增通用 DDL 格式化工具
- DataGrid 查看 DDL 时按数据源方言输出可读 SQL
- 覆盖 DuckDB DDL 展示与工具层测试
This commit is contained in:
Syngnat
2026-06-05 22:21:40 +08:00
parent d2189e1442
commit a5b27820cb
5 changed files with 120 additions and 3 deletions

View File

@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest';
import { formatDdlForDisplay } from './ddlFormat';
describe('formatDdlForDisplay', () => {
it('formats DuckDB create table SQL into multiline output', () => {
const raw = 'CREATE TABLE customers(customer_id BIGINT, customer_code VARCHAR, city VARCHAR, tier VARCHAR, signup_date DATE, lifetime_value DECIMAL(12,2), PRIMARY KEY(customer_id));';
const formatted = formatDdlForDisplay(raw, 'duckdb');
expect(formatted).toContain('CREATE TABLE customers (');
expect(formatted).toContain('customer_id BIGINT,');
expect(formatted).toContain('PRIMARY KEY (customer_id)');
expect(formatted).toContain('\n');
});
it('returns original text when formatter cannot parse the statement', () => {
const raw = 'not valid ddl(';
expect(formatDdlForDisplay(raw, 'duckdb')).toBe(raw);
});
});

View File

@@ -0,0 +1,53 @@
import { format } from 'sql-formatter';
const resolveDdlFormatterLanguage = (dbType: string): string | null => {
const normalized = String(dbType || '').trim().toLowerCase();
switch (normalized) {
case 'duckdb':
return 'duckdb';
case 'sqlite':
return 'sqlite';
case 'postgres':
case 'postgresql':
case 'kingbase':
case 'highgo':
case 'opengauss':
case 'vastbase':
return 'postgresql';
case 'mariadb':
return 'mariadb';
case 'mysql':
case 'sphinx':
return 'mysql';
case 'sqlserver':
return 'transactsql';
case 'oracle':
case 'dameng':
case 'oceanbase':
return 'plsql';
case 'clickhouse':
return 'clickhouse';
default:
return 'sql';
}
};
export const formatDdlForDisplay = (ddlText: unknown, dbType: string): string => {
const raw = String(ddlText ?? '').trim();
if (!raw) {
return '';
}
const language = resolveDdlFormatterLanguage(dbType);
if (!language) {
return raw;
}
try {
return format(raw, {
language,
keywordCase: 'upper',
linesBetweenQueries: 1,
});
} catch {
return raw;
}
};