From d163136b6f3c9a99ffe992c511fd9187a93faa10 Mon Sep 17 00:00:00 2001 From: zipg Date: Tue, 28 Jul 2026 01:17:37 +0800 Subject: [PATCH] fix(sql): split GaussDB procedures with repeated declarations --- .../__tests__/sql/sqlStatementRanges.spec.ts | 40 ++++++++++++++++++ .../desktop/src/lib/sql/sqlStatementRanges.ts | 2 +- crates/dbx-core/src/sql.rs | 42 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts b/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts index eccdda1be2..ded9db00c5 100644 --- a/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts +++ b/apps/desktop/src/lib/__tests__/sql/sqlStatementRanges.spec.ts @@ -92,6 +92,37 @@ END$function$; DROP FUNCTION IF EXISTS dbx_issue_4572_tmp_missing;`; +const gaussDbIssue4573Script = `CREATE OR REPLACE PROCEDURE createIndex ( + dbName IN VARCHAR(32), + tableName IN VARCHAR(64), + indexInfo IN VARCHAR(64), + indexColumns IN VARCHAR(128) +) AS +DECLARE STMT TEXT; + +DECLARE flag int; + +BEGIN +SELECT + count(*) INTO flag +FROM + PG_CATALOG.PG_INDEXES +WHERE + schemaname = dbName + AND TABLENAME = tableName + AND INDEXNAME = indexInfo; + +IF flag = 0 THEN STMT := 'CREATE INDEX ' || indexInfo || ' ON ' || dbName || '.' || tableName || '(' || indexColumns || ')'; + +EXECUTE STMT; + +END IF; + +END; + +SELECT 1 AS after_procedure; +SELECT 2 AS final_statement;`; + const xuguProgrammableObjectFixtures = [ `CREATE OR REPLACE PROCEDURE dbx_xugu_procedure AS v_value INTEGER; @@ -313,6 +344,10 @@ describe("splitSqlStatementRanges", () => { ]); }); + it("separates the issue #4573 GaussDB procedure from following statements", () => { + expect(rangeSqlTexts(splitSqlStatementRanges(gaussDbIssue4573Script, "gaussdb"))).toEqual([gaussDbIssue4573Script.slice(0, gaussDbIssue4573Script.indexOf("\n\nSELECT 1")), "SELECT 1 AS after_procedure", "SELECT 2 AS final_statement"]); + }); + it("keeps Xugu programmable object DDL together and retains its terminator", () => { for (const sql of xuguProgrammableObjectFixtures) { const ranges = splitSqlStatementRanges(`${sql}\nSELECT 1;`, "xugu"); @@ -876,6 +911,11 @@ WHERE request_json LIKE '%"paperFlag":null%';`; expect(statementRangeAtCursor(gaussDbNestedProcedure, outerNull, "gaussdb")?.sql.trim()).toBe(gaussDbNestedProcedure); }); + it("returns only the issue #4573 GaussDB procedure for a gutter cursor", () => { + const expected = gaussDbIssue4573Script.slice(0, gaussDbIssue4573Script.indexOf("\n\nSELECT 1")); + expect(statementRangeAtCursor(gaussDbIssue4573Script, indexOf(gaussDbIssue4573Script, "count(*)"), "gaussdb")?.sql.trim()).toBe(expected); + }); + it("returns the full SAP HANA DO block for cursors inside nested statements", () => { const range = statementRangeAtCursor(sapHanaDoBlockFixture, indexOf(sapHanaDoBlockFixture, "Result"), "saphana"); diff --git a/apps/desktop/src/lib/sql/sqlStatementRanges.ts b/apps/desktop/src/lib/sql/sqlStatementRanges.ts index 3e3042fbcb..184c7746b4 100644 --- a/apps/desktop/src/lib/sql/sqlStatementRanges.ts +++ b/apps/desktop/src/lib/sql/sqlStatementRanges.ts @@ -1705,7 +1705,7 @@ function oraclePlSqlBlockIsComplete(sql: string): boolean { if (token.kind !== "word") continue; if (token.value === "DECLARE") { - stack.push("DECLARATION"); + if (stack[stack.length - 1] !== "DECLARATION") stack.push("DECLARATION"); continue; } if (token.value === "BEGIN") { diff --git a/crates/dbx-core/src/sql.rs b/crates/dbx-core/src/sql.rs index 1ba258cc26..b0e9323305 100644 --- a/crates/dbx-core/src/sql.rs +++ b/crates/dbx-core/src/sql.rs @@ -3221,6 +3221,48 @@ DROP FUNCTION IF EXISTS dbx_issue_4572_tmp_missing;"; assert_eq!(statements[2], "DROP FUNCTION IF EXISTS dbx_issue_4572_tmp_missing"); } + #[test] + fn gaussdb_split_separates_issue_4573_procedure_from_following_statements() { + let sql = "\ +CREATE OR REPLACE PROCEDURE createIndex ( + dbName IN VARCHAR(32), + tableName IN VARCHAR(64), + indexInfo IN VARCHAR(64), + indexColumns IN VARCHAR(128) +) AS +DECLARE STMT TEXT; + +DECLARE flag int; + +BEGIN +SELECT + count(*) INTO flag +FROM + PG_CATALOG.PG_INDEXES +WHERE + schemaname = dbName + AND TABLENAME = tableName + AND INDEXNAME = indexInfo; + +IF flag = 0 THEN STMT := 'CREATE INDEX ' || indexInfo || ' ON ' || dbName || '.' || tableName || '(' || indexColumns || ')'; + +EXECUTE STMT; + +END IF; + +END; + +SELECT 1 AS after_procedure; +SELECT 2 AS final_statement;"; + + let statements = split_sql_statements_for_database(sql, DatabaseType::Gaussdb); + assert_eq!(statements.len(), 3); + assert!(statements[0].starts_with("CREATE OR REPLACE PROCEDURE createIndex")); + assert!(statements[0].ends_with("END;")); + assert_eq!(statements[1], "SELECT 1 AS after_procedure"); + assert_eq!(statements[2], "SELECT 2 AS final_statement"); + } + #[test] fn oracle_like_split_keeps_issue_2405_anonymous_plsql_block_together() { let sql = "\