From ed50842cbebe1f1a299126ad41b66fe37517883c Mon Sep 17 00:00:00 2001 From: yzxcj797 <1784931579@qq.com> Date: Wed, 19 Aug 2026 00:32:52 +0800 Subject: [PATCH] Preserve nested PostgreSQL function defaults Recursively serialize function arguments and casts when converting PostgreSQL default expressions. This keeps nested functions such as the pg_dump default from issue #431 intact instead of dropping their arguments. --- package.json | 3 ++- src/utils/importSQL/postgres.js | 20 +++------------ src/utils/importSQL/postgres.test.js | 38 ++++++++++++++++++++++++++++ src/utils/importSQL/shared.js | 29 ++++++++------------- 4 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 src/utils/importSQL/postgres.test.js diff --git a/package.json b/package.json index 848104764..68f03fa92 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "vite", "build": "vite build", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "preview": "vite preview", + "test": "node --test" }, "dependencies": { "@dagrejs/dagre": "^3.1.1", diff --git a/src/utils/importSQL/postgres.js b/src/utils/importSQL/postgres.js index ddee8b90b..8c73649c9 100644 --- a/src/utils/importSQL/postgres.js +++ b/src/utils/importSQL/postgres.js @@ -75,22 +75,10 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) { if (d.default_val) { let defaultValue = ""; if (d.default_val.value.type === "function") { - defaultValue = d.default_val.value.name.name[0].value; - if (d.default_val.value.args) { - defaultValue += - "(" + - d.default_val.value.args.value - .map((v) => { - if ( - v.type === "single_quote_string" || - v.type === "double_quote_string" - ) - return "'" + v.value + "'"; - return v.value; - }) - .join(", ") + - ")"; - } + defaultValue = buildSQLFromAST( + d.default_val.value, + DB.POSTGRES, + ); } else if (d.default_val.value.type === "null") { defaultValue = "NULL"; } else if (d.default_val.value.type === "cast") { diff --git a/src/utils/importSQL/postgres.test.js b/src/utils/importSQL/postgres.test.js new file mode 100644 index 000000000..f071f8575 --- /dev/null +++ b/src/utils/importSQL/postgres.test.js @@ -0,0 +1,38 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import parserPackage from "node-sql-parser"; +import { createServer } from "vite"; + +const { Parser } = parserPackage; + +const vite = await createServer({ + appType: "custom", + configFile: false, + logLevel: "error", + optimizeDeps: { noDiscovery: true }, + server: { middlewareMode: true }, +}); + +try { + const { fromPostgres } = await vite.ssrLoadModule( + "/src/utils/importSQL/postgres.js", + ); + + test("preserves nested function defaults when importing PostgreSQL", () => { + const sql = `CREATE TABLE public.person ( + id bigint NOT NULL, + external_id character varying(255) + DEFAULT "substring"(md5((random())::text), 1, 6) + );`; + const ast = new Parser().astify(sql, { database: "postgresql" }); + + const diagram = fromPostgres(ast); + const field = diagram.tables[0].fields.find( + (field) => field.name === "external_id", + ); + + assert.equal(field.default, "substring(md5(random()::TEXT), 1, 6)"); + }); +} finally { + await vite.close(); +} diff --git a/src/utils/importSQL/shared.js b/src/utils/importSQL/shared.js index 521a22fbc..9c113f0a3 100644 --- a/src/utils/importSQL/shared.js +++ b/src/utils/importSQL/shared.js @@ -29,25 +29,16 @@ export function buildSQLFromAST(ast, db = DB.MYSQL) { } if (ast.type === "function") { - let expr = ""; - expr = ast.name; - if (ast.args) { - expr += - "(" + - ast.args.value - .map((v) => { - if (v.type === "column_ref") return "`" + v.column + "`"; - if ( - v.type === "single_quote_string" || - v.type === "double_quote_string" - ) - return "'" + v.value + "'"; - return v.value; - }) - .join(", ") + - ")"; - } - return expr; + const name = Array.isArray(ast.name?.name) + ? ast.name.name.map((part) => part.value).join(".") + : ast.name; + const args = Array.isArray(ast.args?.value) + ? ast.args.value.map((arg) => buildSQLFromAST(arg, db)).join(", ") + : ""; + return `${name}(${args})`; + } else if (ast.type === "cast") { + const type = (ast.target ?? []).map((target) => target.dataType).join(", "); + return `${buildSQLFromAST(ast.expr, db)}::${type}`; } else if (ast.type === "column_ref") { return quoteColumn(ast.column, db); } else if (ast.type === "expr_list") {