fix(sql-format): format source and DDL with proper dialects

This commit is contained in:
kkk000111999 authored and GitHub committed 2026-06-27 12:36:13 +08:00
1 parent ed35164828
commit ff810f3058
7 files changed
+136 -56

No files matched your search

@@ -63,7 +63,7 @@ import type { SqlExecutionOverride } from "@/lib/sqlExecutionTarget";
import type { DataGridSortMode } from "@/lib/dataGridSort"; import type { DataGridSortMode } from "@/lib/dataGridSort";
import { useTabScroll } from "@/composables/useTabScroll"; import { useTabScroll } from "@/composables/useTabScroll";
import type { QueryTab, ConnectionConfig, TableInfoTab, TreeNode, VectorCollectionMeta } from "@/types/database"; import type { QueryTab, ConnectionConfig, TableInfoTab, TreeNode, VectorCollectionMeta } from "@/types/database";
import type { SqlFormatDialect } from "@/lib/sqlFormatter"; import { sqlFormatDialectForDbType, type SqlFormatDialect } from "@/lib/sqlFormatter";
type DataGridHandle = { type DataGridHandle = {
onToolbarRefresh: () => Promise<void> | void; onToolbarRefresh: () => Promise<void> | void;
@@ -196,23 +196,7 @@ const activeTabDimension = computed(() => {
return meta && "dimension" in meta ? (meta as VectorCollectionMeta).dimension : undefined; return meta && "dimension" in meta ? (meta as VectorCollectionMeta).dimension : undefined;
}); });
const activeSqlFormatDialect = computed<SqlFormatDialect>(() => { const activeSqlFormatDialect = computed<SqlFormatDialect>(() => sqlFormatDialectForDbType(activeEffectiveDatabaseType.value));
switch (activeEffectiveDatabaseType.value) {
case "mysql":
return "mysql";
case "postgres":
case "kwdb":
return "postgres";
case "sqlite":
case "rqlite":
case "turso":
return "sqlite";
case "sqlserver":
return "sqlserver";
default:
return "generic";
}
});
const editorDialect = computed<"mysql" | "postgres" | "sqlserver">(() => { const editorDialect = computed<"mysql" | "postgres" | "sqlserver">(() => {
if (activeEffectiveDatabaseType.value === "postgres" || activeEffectiveDatabaseType.value === "kwdb") return "postgres"; if (activeEffectiveDatabaseType.value === "postgres" || activeEffectiveDatabaseType.value === "kwdb") return "postgres";
@@ -8,6 +8,7 @@ import { useSettingsStore } from "@/stores/settingsStore";
import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes"; import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes";
import { createDbxCodeMirrorSqlDialect } from "@/lib/codemirrorSqlDialect"; import { createDbxCodeMirrorSqlDialect } from "@/lib/codemirrorSqlDialect";
import { copyToClipboard } from "@/lib/clipboard"; import { copyToClipboard } from "@/lib/clipboard";
import { formatSqlForDisplay, type SqlFormatDialect } from "@/lib/sqlFormatter";
import * as api from "@/lib/api"; import * as api from "@/lib/api";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
@@ -23,6 +24,8 @@ const props = withDefaults(
tableName: string; tableName: string;
/** SQL dialect for syntax highlighting. Non-PG/non-MSSQL databases fall back to MySQL (same as QueryEditor's source viewer). */ /** SQL dialect for syntax highlighting. Non-PG/non-MSSQL databases fall back to MySQL (same as QueryEditor's source viewer). */
dialect: "mysql" | "postgres" | "sqlserver"; dialect: "mysql" | "postgres" | "sqlserver";
/** SQL formatter dialect. Kept separate from the syntax-highlighting dialect because several PG-compatible DBs highlight as MySQL. */
formatDialect?: SqlFormatDialect;
}>(), }>(),
{}, {},
); );
@@ -54,7 +57,7 @@ watch(
try { try {
const schema = props.schema || props.database; const schema = props.schema || props.database;
const ddl = await api.getTableDdl(props.connectionId, props.database, schema, props.tableName); const ddl = await api.getTableDdl(props.connectionId, props.database, schema, props.tableName);
ddlContent.value = ddl; ddlContent.value = await formatSqlForDisplay(ddl, props.formatDialect ?? props.dialect, settingsStore.editorSettings.sqlFormatter);
} catch (e: any) { } catch (e: any) {
ddlError.value = e?.message || String(e); ddlError.value = e?.message || String(e);
} finally { } finally {
@@ -160,8 +163,8 @@ function retry() {
const schema = props.schema || props.database; const schema = props.schema || props.database;
api api
.getTableDdl(props.connectionId, props.database, schema, props.tableName) .getTableDdl(props.connectionId, props.database, schema, props.tableName)
.then((ddl) => { .then(async (ddl) => {
ddlContent.value = ddl; ddlContent.value = await formatSqlForDisplay(ddl, props.formatDialect ?? props.dialect, settingsStore.editorSettings.sqlFormatter);
}) })
.catch((e: any) => { .catch((e: any) => {
ddlError.value = e?.message || String(e); ddlError.value = e?.message || String(e);
@@ -67,7 +67,7 @@ import { useSettingsStore } from "@/stores/settingsStore";
import { useQueryStore } from "@/stores/queryStore"; import { useQueryStore } from "@/stores/queryStore";
import QueryEditor from "@/components/editor/QueryEditor.vue"; import QueryEditor from "@/components/editor/QueryEditor.vue";
import DdlViewDialog from "./DdlViewDialog.vue"; import DdlViewDialog from "./DdlViewDialog.vue";
import type { SqlFormatDialect } from "@/lib/sqlFormatter"; import { formatSqlForDisplay, sqlFormatDialectForDbType, type SqlFormatDialect } from "@/lib/sqlFormatter";
import { isCancelSearchShortcut } from "@/lib/keyboardShortcuts"; import { isCancelSearchShortcut } from "@/lib/keyboardShortcuts";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { import {
@@ -122,6 +122,7 @@ const sourceRow = ref<ObjectBrowserRow | null>(null);
const sourceEditing = ref(false); const sourceEditing = ref(false);
const effectiveDatabaseType = computed(() => effectiveDatabaseTypeForConnection(props.connection) ?? props.connection.db_type); const effectiveDatabaseType = computed(() => effectiveDatabaseTypeForConnection(props.connection) ?? props.connection.db_type);
const tableStructureDatabaseType = computed(() => tableStructureDatabaseTypeForConnection(props.connection) ?? props.connection.db_type); const tableStructureDatabaseType = computed(() => tableStructureDatabaseTypeForConnection(props.connection) ?? props.connection.db_type);
const sourceEditableText = ref("");
const sourceDraft = ref(""); const sourceDraft = ref("");
const sourceSaving = ref(false); const sourceSaving = ref(false);
const sourceSaveError = ref(""); const sourceSaveError = ref("");
@@ -178,25 +179,7 @@ const canOpenDiagram = computed(() => !!props.database && supportsSchemaDiagram(
const canOpenTableImport = computed(() => !!props.database && supportsTableImport(effectiveDatabaseType.value)); const canOpenTableImport = computed(() => !!props.database && supportsTableImport(effectiveDatabaseType.value));
const supportsTruncateTable = computed(() => supportsTableTruncate(effectiveDatabaseType.value)); const supportsTruncateTable = computed(() => supportsTableTruncate(effectiveDatabaseType.value));
const sourceDialect = computed(() => codeMirrorSqlDialect(effectiveDatabaseType.value)); const sourceDialect = computed(() => codeMirrorSqlDialect(effectiveDatabaseType.value));
const sourceFormatDialect = computed<SqlFormatDialect>(() => { const sourceFormatDialect = computed<SqlFormatDialect>(() => sqlFormatDialectForDbType(effectiveDatabaseType.value));
switch (effectiveDatabaseType.value) {
case "mysql":
case "postgres":
case "sqlite":
case "sqlserver":
return effectiveDatabaseType.value;
case "rqlite":
case "turso":
return "sqlite";
case "gaussdb":
case "kwdb":
case "opengauss":
case "questdb":
return "postgres";
default:
return "generic";
}
});
const objectFilters = computed<ObjectFilter[]>(() => const objectFilters = computed<ObjectFilter[]>(() =>
( (
[ [
@@ -425,6 +408,7 @@ async function openSource(row: ObjectBrowserRow) {
sourceContent.value = ""; sourceContent.value = "";
sourceError.value = ""; sourceError.value = "";
sourceEditing.value = false; sourceEditing.value = false;
sourceEditableText.value = "";
sourceDraft.value = ""; sourceDraft.value = "";
sourceSaveError.value = ""; sourceSaveError.value = "";
sourceLoading.value = true; sourceLoading.value = true;
@@ -437,7 +421,8 @@ async function openSource(row: ObjectBrowserRow) {
name: row.name, name: row.name,
source: result.source, source: result.source,
}); });
sourceContent.value = editable; sourceEditableText.value = editable;
sourceContent.value = await formatSqlForDisplay(editable, sourceFormatDialect.value, settingsStore.editorSettings.sqlFormatter);
sourceDraft.value = editable; sourceDraft.value = editable;
sourceEditing.value = row.type !== "SEQUENCE"; sourceEditing.value = row.type !== "SEQUENCE";
} catch (e: any) { } catch (e: any) {
@@ -457,8 +442,9 @@ async function openViewDdl(row: ObjectBrowserRow) {
name: row.name, name: row.name,
source: result.source, source: result.source,
}); });
const formatted = await formatSqlForDisplay(ddl, sourceFormatDialect.value, settingsStore.editorSettings.sqlFormatter);
const tabId = queryStore.createTab(props.connection.id, props.database, `DDL - ${row.name}`); const tabId = queryStore.createTab(props.connection.id, props.database, `DDL - ${row.name}`);
queryStore.updateSql(tabId, ddl); queryStore.updateSql(tabId, formatted);
} catch (e: any) { } catch (e: any) {
toast(e?.message || String(e), 5000); toast(e?.message || String(e), 5000);
} }
@@ -630,6 +616,7 @@ function closeSource() {
sourceContent.value = ""; sourceContent.value = "";
sourceError.value = ""; sourceError.value = "";
sourceEditing.value = false; sourceEditing.value = false;
sourceEditableText.value = "";
sourceDraft.value = ""; sourceDraft.value = "";
sourceSaveError.value = ""; sourceSaveError.value = "";
} }
@@ -1040,8 +1027,8 @@ async function copySource() {
} }
function editSource() { function editSource() {
if (!sourceRow.value || !sourceContent.value) return; if (!sourceRow.value || !sourceEditableText.value) return;
sourceDraft.value = sourceContent.value; sourceDraft.value = sourceEditableText.value;
sourceSaveError.value = ""; sourceSaveError.value = "";
sourceEditing.value = true; sourceEditing.value = true;
} }
@@ -1684,7 +1671,7 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<DdlViewDialog v-if="ddlDialogTarget" :connection-id="props.connection.id" :database="props.database" :schema="ddlDialogTarget.schema || selectedSchema" :table-name="ddlDialogTarget.name" :dialect="sourceDialect" v-model:open="showDdlDialog" /> <DdlViewDialog v-if="ddlDialogTarget" :connection-id="props.connection.id" :database="props.database" :schema="ddlDialogTarget.schema || selectedSchema" :table-name="ddlDialogTarget.name" :dialect="sourceDialect" :format-dialect="sourceFormatDialect" v-model:open="showDdlDialog" />
</template> </template>
<style scoped> <style scoped>
@@ -100,6 +100,7 @@ import {
import { buildRenameObjectSql, supportsObjectRename, type RenameableObjectType } from "@/lib/objectRenameSql"; import { buildRenameObjectSql, supportsObjectRename, type RenameableObjectType } from "@/lib/objectRenameSql";
import { buildRoutineRenameObjectSourceStatements, supportsSourceBackedRoutineRename } from "@/lib/objectSourceEditor"; import { buildRoutineRenameObjectSourceStatements, supportsSourceBackedRoutineRename } from "@/lib/objectSourceEditor";
import { buildViewDdl } from "@/lib/viewDdl"; import { buildViewDdl } from "@/lib/viewDdl";
import { formatSqlForDisplay, sqlFormatDialectForDbType } from "@/lib/sqlFormatter";
import DdlViewDialog from "@/components/objects/DdlViewDialog.vue"; import DdlViewDialog from "@/components/objects/DdlViewDialog.vue";
import { getTableStructureCapabilities } from "@/lib/tableStructureCapabilities"; import { getTableStructureCapabilities } from "@/lib/tableStructureCapabilities";
import { codeMirrorSqlDialect, connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect"; import { codeMirrorSqlDialect, connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
@@ -1278,7 +1279,8 @@ async function generateDdlTemplate() {
source: result.source, source: result.source,
}); });
} }
openSqlTemplateTab(node.connectionId, node.database, node.schema, ddl, `DDL - ${node.label}`); const formatted = await formatSqlForDisplay(ddl, sqlFormatDialectForDbType(currentDatabaseType()), settingsStore.editorSettings.sqlFormatter);
openSqlTemplateTab(node.connectionId, node.database, node.schema, formatted, `DDL - ${node.label}`);
} catch (e: any) { } catch (e: any) {
toast(e?.message || String(e), 5000); toast(e?.message || String(e), 5000);
} }
@@ -1452,6 +1454,10 @@ const ddlDialect = computed(() => {
if (!ddlTarget.value?.connectionId) return "mysql"; if (!ddlTarget.value?.connectionId) return "mysql";
return codeMirrorSqlDialect(effectiveDatabaseTypeForConnection(connectionStore.getConfig(ddlTarget.value.connectionId))); return codeMirrorSqlDialect(effectiveDatabaseTypeForConnection(connectionStore.getConfig(ddlTarget.value.connectionId)));
}); });
const ddlFormatDialect = computed(() => {
if (!ddlTarget.value?.connectionId) return "generic";
return sqlFormatDialectForDbType(effectiveDatabaseTypeForConnection(connectionStore.getConfig(ddlTarget.value.connectionId)));
});
const showCreateDatabaseDialog = ref(false); const showCreateDatabaseDialog = ref(false);
const createDatabaseName = ref(""); const createDatabaseName = ref("");
const createDatabaseCharset = ref("utf8mb4"); const createDatabaseCharset = ref("utf8mb4");
@@ -1619,7 +1625,8 @@ function viewObjectSource() {
}) })
.then(async (result) => { .then(async (result) => {
const tabId = queryStore.createTab(node.connectionId!, node.database!, `Source - ${node.label}`); const tabId = queryStore.createTab(node.connectionId!, node.database!, `Source - ${node.label}`);
queryStore.updateSql(tabId, result.source); const formatted = await formatSqlForDisplay(result.source, sqlFormatDialectForDbType(currentDatabaseType()), settingsStore.editorSettings.sqlFormatter);
queryStore.updateSql(tabId, formatted);
if (objectType !== "SEQUENCE") { if (objectType !== "SEQUENCE") {
queryStore.setObjectSource(tabId, { queryStore.setObjectSource(tabId, {
schema, schema,
@@ -1653,8 +1660,9 @@ function viewObjectDdl() {
name: node.label, name: node.label,
source: result.source, source: result.source,
}); });
const formatted = await formatSqlForDisplay(ddl, sqlFormatDialectForDbType(effectiveDatabaseTypeForConnection(connection)), settingsStore.editorSettings.sqlFormatter);
const tabId = queryStore.createTab(node.connectionId!, node.database!, `DDL - ${node.label}`); const tabId = queryStore.createTab(node.connectionId!, node.database!, `DDL - ${node.label}`);
queryStore.updateSql(tabId, ddl); queryStore.updateSql(tabId, formatted);
}) })
.catch((e: any) => { .catch((e: any) => {
toast(e?.message || String(e), 5000); toast(e?.message || String(e), 5000);
@@ -4432,7 +4440,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
<DangerConfirmDialog v-model:open="showDropSchemaConfirm" :title="t('contextMenu.confirmDropSchemaTitle')" :message="t('contextMenu.confirmDropSchemaMessage', { name: node.label })" :sql="dropSchemaPreviewSql" :confirm-label="t('contextMenu.dropSchema')" @confirm="confirmDropSchema" /> <DangerConfirmDialog v-model:open="showDropSchemaConfirm" :title="t('contextMenu.confirmDropSchemaTitle')" :message="t('contextMenu.confirmDropSchemaMessage', { name: node.label })" :sql="dropSchemaPreviewSql" :confirm-label="t('contextMenu.dropSchema')" @confirm="confirmDropSchema" />
<DdlViewDialog v-if="ddlTarget" :connection-id="ddlTarget.connectionId!" :database="ddlTarget.database!" :schema="ddlTarget.schema" :table-name="ddlTarget.label" :dialect="ddlDialect" v-model:open="showDdlDialog" /> <DdlViewDialog v-if="ddlTarget" :connection-id="ddlTarget.connectionId!" :database="ddlTarget.database!" :schema="ddlTarget.schema" :table-name="ddlTarget.label" :dialect="ddlDialect" :format-dialect="ddlFormatDialect" v-model:open="showDdlDialog" />
</template> </template>
<style> <style>
@@ -20,6 +20,7 @@ import { useTheme } from "@/composables/useTheme";
import { useToast } from "@/composables/useToast"; import { useToast } from "@/composables/useToast";
import { type SqlHighlighter, createShikiSqlHighlighter } from "@/lib/sqlHighlighter"; import { type SqlHighlighter, createShikiSqlHighlighter } from "@/lib/sqlHighlighter";
import { copyToClipboard } from "@/lib/clipboard"; import { copyToClipboard } from "@/lib/clipboard";
import { formatSqlForDisplay, sqlFormatDialectForDbType } from "@/lib/sqlFormatter";
import { queryTimeoutSecsForConnection } from "@/lib/queryTimeout"; import { queryTimeoutSecsForConnection } from "@/lib/queryTimeout";
import { type EditableStructureColumn, type EditableStructureForeignKey, type EditableStructureIndex, type EditableStructureTrigger } from "@/lib/tableStructureEditorSql"; import { type EditableStructureColumn, type EditableStructureForeignKey, type EditableStructureIndex, type EditableStructureTrigger } from "@/lib/tableStructureEditorSql";
import { PRESET_FIELDS_TEMPLATE_ID, createTableColumnTemplateDrafts } from "@/lib/tableColumnTemplates"; import { PRESET_FIELDS_TEMPLATE_ID, createTableColumnTemplateDrafts } from "@/lib/tableColumnTemplates";
@@ -101,7 +102,8 @@ async function fetchDdl() {
if (!props.connectionId || !props.database || !props.tableName || ddlFetched.value || !tableMetadataCapabilities.value.ddl) return; if (!props.connectionId || !props.database || !props.tableName || ddlFetched.value || !tableMetadataCapabilities.value.ddl) return;
ddlLoading.value = true; ddlLoading.value = true;
try { try {
ddlContent.value = await api.getTableDdl(props.connectionId, props.database, metadataSchema.value, props.tableName); const ddl = await api.getTableDdl(props.connectionId, props.database, metadataSchema.value, props.tableName);
ddlContent.value = await formatSqlForDisplay(ddl, sqlFormatDialectForDbType(databaseType.value), settingsStore.editorSettings.sqlFormatter);
ddlFetched.value = true; ddlFetched.value = true;
} catch (e: any) { } catch (e: any) {
ddlContent.value = `-- Error: ${e?.message || e}`; ddlContent.value = `-- Error: ${e?.message || e}`;
@@ -794,7 +796,7 @@ async function loadStructure(silent = false, scope: StructureRefreshScope = FULL
if (databaseType.value === "manticoresearch" && tableMetadataCapabilities.value.ddl) { if (databaseType.value === "manticoresearch" && tableMetadataCapabilities.value.ddl) {
try { try {
const ddl = await api.getTableDdl(connectionId, database, schema, tableName); const ddl = await api.getTableDdl(connectionId, database, schema, tableName);
ddlContent.value = ddl; ddlContent.value = await formatSqlForDisplay(ddl, sqlFormatDialectForDbType(databaseType.value), settingsStore.editorSettings.sqlFormatter);
ddlFetched.value = true; ddlFetched.value = true;
nextColumns = applyManticoreDdlColumnExtras(nextColumns, ddl); nextColumns = applyManticoreDdlColumnExtras(nextColumns, ddl);
} catch { } catch {
@@ -0,0 +1,30 @@
import { describe, expect, it } from "vitest";
import { formatSqlForDisplay, formatSqlText, MAX_SQL_FORMAT_CHARS, sqlFormatDialectForDbType } from "@/lib/sqlFormatter";
describe("sqlFormatter", () => {
it("maps PostgreSQL-compatible database types to the postgres formatter dialect", () => {
for (const dbType of ["postgres", "kwdb", "gaussdb", "opengauss", "questdb", "kingbase", "highgo", "vastbase", "redshift"]) {
expect(sqlFormatDialectForDbType(dbType)).toBe("postgres");
}
});
it("maps SQLite-compatible database types to the sqlite formatter dialect", () => {
for (const dbType of ["sqlite", "rqlite", "turso"]) {
expect(sqlFormatDialectForDbType(dbType)).toBe("sqlite");
}
});
it("falls back to the postgres formatter when the generic dialect cannot parse SQL", async () => {
const formatted = await formatSqlText("SELECT 1::int AS id;", "generic");
expect(formatted).toContain("1::int");
expect(formatted).toContain("AS id");
});
it("returns the original SQL for display when formatting fails", async () => {
const oversizedSql = "x".repeat(MAX_SQL_FORMAT_CHARS + 1);
await expect(formatSqlText(oversizedSql, "postgres")).rejects.toThrow("SQL is too large to format safely.");
await expect(formatSqlForDisplay(oversizedSql, "postgres")).resolves.toBe(oversizedSql);
});
});
+70 -4
View File
@@ -4,6 +4,40 @@ export type SqlFormatDialect = "mysql" | "postgres" | "sqlite" | "sqlserver" | "
export const MAX_SQL_FORMAT_CHARS = 1_000_000; export const MAX_SQL_FORMAT_CHARS = 1_000_000;
/**
* Maps a connection's database type to the SQL-formatter dialect to use.
*
* Postgres-compatible engines (GaussDB/openGauss/Kingbase/...) reuse the
* "postgres" grammar, SQLite-compatible ones reuse "sqlite", and anything
* unrecognized falls back to the permissive "generic" dialect. Centralized
* here so every surface that formats SQL (editor, object source, DDL viewers)
* stays in sync.
*/
export function sqlFormatDialectForDbType(dbType: string | null | undefined): SqlFormatDialect {
switch (dbType) {
case "mysql":
return "mysql";
case "postgres":
case "kwdb":
case "gaussdb":
case "opengauss":
case "questdb":
case "kingbase":
case "highgo":
case "vastbase":
case "redshift":
return "postgres";
case "sqlite":
case "rqlite":
case "turso":
return "sqlite";
case "sqlserver":
return "sqlserver";
default:
return "generic";
}
}
function formatterLanguage(dialect: SqlFormatDialect) { function formatterLanguage(dialect: SqlFormatDialect) {
switch (dialect) { switch (dialect) {
case "mysql": case "mysql":
@@ -26,8 +60,40 @@ export async function formatSqlText(sql: string, dialect: SqlFormatDialect = "ge
} }
const { format } = await import("sql-formatter"); const { format } = await import("sql-formatter");
return format(sql, { const options = sqlFormatterOptions(settings);
language: formatterLanguage(dialect), const language = formatterLanguage(dialect);
...sqlFormatterOptions(settings), try {
}); return format(sql, { language, ...options });
} catch (err) {
// The generic "sql" dialect can't parse many real-world constructs (PostgreSQL
// `::` casts, GaussDB/openGauss materialized-view DDL, T-SQL specifics, ...).
// Retry once with the more permissive PostgreSQL grammar, which is a superset
// that tolerates most of these, before surfacing the failure.
if (language !== "postgresql") {
try {
return format(sql, { language: "postgresql", ...options });
} catch {
// fall through to the original error below
}
}
throw err;
}
}
/**
* Format SQL for *display* (object source, view/table DDL viewers).
*
* Unlike `formatSqlText`, this never throws: if the SQL can't be parsed by the
* formatter (vendor-specific DDL, oversized input, ...) the original text is
* returned unchanged so the viewer still shows the source. Use this for
* read-only/auto-format surfaces; use `formatSqlText` where a thrown error
* should surface to the user (e.g. the explicit "Format SQL" command).
*/
export async function formatSqlForDisplay(sql: string, dialect: SqlFormatDialect = "generic", settings: Partial<SqlFormatterSettings> = DEFAULT_SQL_FORMATTER_SETTINGS): Promise<string> {
if (!sql.trim()) return sql;
try {
return await formatSqlText(sql, dialect, settings);
} catch {
return sql;
}
} }