From 88ba58a79c6182f6e0b2767d2e7c150c8f668736 Mon Sep 17 00:00:00 2001 From: zipg Date: Sun, 20 Sep 2026 11:06:40 +0800 Subject: [PATCH] fix(sql-analysis): tolerate Oracle admin DDL in diagnostics --- .../__tests__/sql/semantic/completion.spec.ts | 10 +- .../sql/sqlCompletion.context.spec.ts | 29 +++- apps/desktop/src/lib/sql/sqlCompletion.ts | 36 +++-- crates/dbx-core/tests/sql_analysis.rs | 59 ++++++++ crates/dbx-sql/src/sql.rs | 7 + crates/dbx-sql/src/sql_analysis.rs | 68 ++++++++- ...-9104-oracle-admin-ddl-no-syntax-error.png | Bin 0 -> 36965 bytes .../issue-9525-table-alias-implicit-form.png | Bin 0 -> 47186 bytes packages/app-tests/sqlCompletion.test.ts | 131 ++++++++---------- 9 files changed, 241 insertions(+), 99 deletions(-) create mode 100644 docs/screenshots/issue-9104-oracle-admin-ddl-no-syntax-error.png create mode 100644 docs/screenshots/issue-9525-table-alias-implicit-form.png diff --git a/apps/desktop/src/lib/__tests__/sql/semantic/completion.spec.ts b/apps/desktop/src/lib/__tests__/sql/semantic/completion.spec.ts index d46807a54f..c267556bdb 100644 --- a/apps/desktop/src/lib/__tests__/sql/semantic/completion.spec.ts +++ b/apps/desktop/src/lib/__tests__/sql/semantic/completion.spec.ts @@ -507,7 +507,7 @@ FROM ( autoAliasTables: true, }); - expect(items.find((item) => item.label === "order_items")?.apply).toBe("order_items AS oi2"); + expect(items.find((item) => item.label === "order_items")?.apply).toBe("order_items oi2"); }); it("omits generated aliases on a DELETE target table (issue #9186)", () => { @@ -576,7 +576,7 @@ FROM ( autoAliasTables: true, }); - expect(items.find((item) => item.label === "order_items")?.apply).toBe("order_items AS oi"); + expect(items.find((item) => item.label === "order_items")?.apply).toBe("order_items oi"); }); it("keeps generated aliases after a multi-table DELETE target list", () => { @@ -585,7 +585,7 @@ FROM ( autoAliasTables: true, }); - expect(items.find((item) => item.label === "order_items")?.apply).toBe("order_items AS oi"); + expect(items.find((item) => item.label === "order_items")?.apply).toBe("order_items oi"); }); it("keeps generated aliases on FROM and JOIN sources", () => { @@ -598,8 +598,8 @@ FROM ( autoAliasTables: true, }); - expect(joined.items.find((item) => item.label === "order_items")?.apply).toBe("order_items AS oi"); - expect(queried.items.find((item) => item.label === "DH_MODEL_CAP")?.apply).toBe("DH_MODEL_CAP AS dmc"); + expect(joined.items.find((item) => item.label === "order_items")?.apply).toBe("order_items oi"); + expect(queried.items.find((item) => item.label === "DH_MODEL_CAP")?.apply).toBe("DH_MODEL_CAP dmc"); }); it("preserves dialect-aware identifier quoting in apply text", () => { diff --git a/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts b/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts index 49edce3c7d..4f46ce1ef1 100644 --- a/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts +++ b/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts @@ -674,7 +674,7 @@ describe("sqlCompletion table aliases", () => { }); const table = items.find((item) => item.label === "materials_order_item" && item.type === "table"); - expect(table?.apply).toBe("materials_order_item AS moi"); + expect(table?.apply).toBe("materials_order_item moi"); }); it("uses every word initial for longer multi-word names", () => { @@ -686,7 +686,7 @@ describe("sqlCompletion table aliases", () => { }); const table = items.find((item) => item.label === "super_long_customer_order_history_archive_snapshot_daily_replica" && item.type === "table"); - expect(table?.apply).toBe("super_long_customer_order_history_archive_snapshot_daily_replica AS slcohasdr"); + expect(table?.apply).toBe("super_long_customer_order_history_archive_snapshot_daily_replica slcohasdr"); }); it("applies generated aliases to table completions when enabled", () => { @@ -698,7 +698,7 @@ describe("sqlCompletion table aliases", () => { }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); - expect(table?.apply).toBe("order_items AS oi"); + expect(table?.apply).toBe("order_items oi"); }); it("omits AS from Oracle table alias completions", () => { @@ -772,7 +772,7 @@ describe("sqlCompletion table aliases", () => { }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); - expect(table?.apply).toBe("order_items AS oi2"); + expect(table?.apply).toBe("order_items oi2"); }); it("applies generated aliases in comma-separated FROM table lists", () => { @@ -784,7 +784,7 @@ describe("sqlCompletion table aliases", () => { }); const table = items.find((item) => item.label === "order_items" && item.type === "table"); - expect(table?.apply).toBe("order_items AS oi"); + expect(table?.apply).toBe("order_items oi"); }); it("does not apply generated aliases to non-query table completions", () => { @@ -798,6 +798,25 @@ describe("sqlCompletion table aliases", () => { const table = items.find((item) => item.label === "order_items" && item.type === "table"); expect(table?.apply).toBe("order_items"); }); + + it("emits table aliases without AS on every dialect (issue #9525)", () => { + // `FROM orders AS o` is rejected by Oracle and the Oracle-compatible profiles, while the + // implicit form is accepted everywhere, so generated SQL must use it for all dialects. + for (const databaseType of ["postgres", "mysql", "sqlserver", "oracle", "sqlite", "clickhouse"] as const) { + const sql = "SELECT * FROM ord"; + const items = buildSqlCompletionItems(sql, sql.length, { + databaseType, + tables: [{ name: "order_items", type: "table" }], + columnsByTable: new Map(), + autoAliasTables: true, + }); + + const table = items.find((item) => item.label === "order_items" && item.type === "table"); + // Oracle additionally double-quotes the identifier to preserve its stored case. + expect(table?.apply, databaseType).not.toContain(" AS "); + expect(table?.apply, databaseType).toMatch(/order_items"? oi$/); + } + }); }); describe("sqlCompletion scoped context classification", () => { diff --git a/apps/desktop/src/lib/sql/sqlCompletion.ts b/apps/desktop/src/lib/sql/sqlCompletion.ts index 5298c8ad82..1d4d04f920 100644 --- a/apps/desktop/src/lib/sql/sqlCompletion.ts +++ b/apps/desktop/src/lib/sql/sqlCompletion.ts @@ -1690,13 +1690,13 @@ class SqlCompletionProvider { const emptyTableNameCompletion = !context.prefix && (context.suggestTables || context.exclusiveTableSuggestions); if (!pendingJoinKeyword && !emptyTableNameCompletion && !context.tableAliasAfterCursor && context.referencedTables.length > 0 && !context.suggestColumns && !context.insertTable && supportsTableAliases(this.databaseType)) { - this.items.push(...buildAliasItems(context, this.databaseType, this.input.keywordCase)); + this.items.push(...buildAliasItems(context)); } if (!context.exclusiveColumnSuggestions && context.suggestTables) { const autoAliasTables = !!this.input.autoAliasTables && context.autoAliasTableCompletions && !context.tableCompletionTargetAliasUnsafe && supportsTableAliases(this.databaseType); - this.items.push(...buildForeignKeyRelatedTableItems(context, completionTables, this.input.foreignKeysByTable, this.dialect, autoAliasTables, this.databaseType, this.input.keywordCase, this.input.currentSchema)); - this.items.push(...buildTableItems(context, completionTables, this.dialect, autoAliasTables, context.referencedTables, this.databaseType, this.input.currentSchema, this.input.keywordCase)); + this.items.push(...buildForeignKeyRelatedTableItems(context, completionTables, this.input.foreignKeysByTable, this.dialect, autoAliasTables, this.databaseType, this.input.currentSchema)); + this.items.push(...buildTableItems(context, completionTables, this.dialect, autoAliasTables, context.referencedTables, this.databaseType, this.input.currentSchema)); if (this.databaseType === "clickhouse") { this.items.push(...buildClickHouseFunctionItems(context.prefix, context.openingParenAfterCursor, "table")); } @@ -1725,7 +1725,7 @@ class SqlCompletionProvider { if (context.prefix) { for (const item of this.items) { // Alias snippets reuse the prefix as a label while applying alias SQL, so they are not exact name matches. - const isAliasSnippet = item.type === "snippet" && item.apply === formatAliasCompletionApply(item.label, this.databaseType, this.input.keywordCase); + const isAliasSnippet = item.type === "snippet" && item.apply === formatAliasCompletionApply(item.label); const isExactLabelMatch = !isAliasSnippet && item.label.toLowerCase() === context.prefix.toLowerCase(); const isExactFilterTextMatch = item.filterText?.toLowerCase() === context.prefix.toLowerCase(); if (isExactLabelMatch || isExactFilterTextMatch) { @@ -3850,7 +3850,6 @@ function buildTableItems( referencedTables: SqlCompletionReferencedTable[] = [], databaseType?: DatabaseType, currentSchema?: string, - keywordCase?: SqlKeywordCase, ): SqlCompletionItem[] { const { prefix } = context; const qualifierSchema = context.qualifier?.split(".").filter(Boolean).pop(); @@ -3873,7 +3872,7 @@ function buildTableItems( label: table.name, type: "table" as const, detail, - apply: formatTableAliasApply(applyName, alias, databaseType, keywordCase), + apply: formatTableAliasApply(applyName, alias), boost: computeBoost(table.name, prefix) + 1000 + (table.boost ?? 0), dedupeKey: table.applyName || ambiguousTableName || (databaseType === "oracle" && table.schema) ? applyName : undefined, }; @@ -3889,7 +3888,6 @@ function buildForeignKeyRelatedTableItems( dialect?: SqlCompletionApplyDialect, autoAliasTables = false, databaseType?: DatabaseType, - keywordCase?: SqlKeywordCase, currentSchema?: string, ): SqlCompletionItem[] { if (!foreignKeysByTable || context.referencedTables.length === 0) return []; @@ -3928,7 +3926,7 @@ function buildForeignKeyRelatedTableItems( label: table.name, type: "table" as const, detail, - apply: formatTableAliasApply(applyName, alias, databaseType, keywordCase), + apply: formatTableAliasApply(applyName, alias), boost: computeBoost(table.name, context.prefix) + 3600, // Mirror buildTableItems' dedupeKey so an FK candidate and the regular // candidate for the same schema-qualified table collapse to one entry. @@ -4443,7 +4441,7 @@ function buildReferencedAliasItems(context: SqlCompletionContext, t?: SqlComplet return items; } -function buildAliasItems(context: SqlCompletionContext, databaseType?: DatabaseType, keywordCase?: SqlKeywordCase): SqlCompletionItem[] { +function buildAliasItems(context: SqlCompletionContext): SqlCompletionItem[] { const items: SqlCompletionItem[] = []; const existingAliases = new Set(context.referencedTables.map((ref) => ref.alias?.toLowerCase()).filter((alias): alias is string => !!alias)); const seen = new Set(existingAliases); @@ -4457,20 +4455,28 @@ function buildAliasItems(context: SqlCompletionContext, databaseType?: DatabaseT label: candidate, type: "snippet" as const, detail: `alias for ${ref.name}`, - apply: formatAliasCompletionApply(candidate, databaseType, keywordCase), + apply: formatAliasCompletionApply(candidate), boost: 1600 - items.length, }); } return items; } -function formatTableAliasApply(tableName: string, alias: string, databaseType?: DatabaseType, keywordCase?: SqlKeywordCase): string { +/** + * Table aliases are always emitted in the implicit form (`orders o`). + * + * `AS` is not valid for table aliases on every engine: Oracle, and the Oracle-compatible profiles + * (OceanBase Oracle, Kingbase in Oracle mode, ...), reject `FROM orders AS o`. The implicit form is + * accepted by every dialect DBX supports, so generated SQL stays portable across + * PostgreSQL / Oracle / Kingbase (issue #9525). + */ +function formatTableAliasApply(tableName: string, alias: string): string { if (!alias) return tableName; - return isOracleLikeDatabase(databaseType) ? `${tableName} ${alias}` : `${tableName} ${applySqlKeywordCase("AS", keywordCase)} ${alias}`; + return `${tableName} ${alias}`; } -function formatAliasCompletionApply(alias: string, databaseType?: DatabaseType, keywordCase?: SqlKeywordCase): string { - return isOracleLikeDatabase(databaseType) ? `${alias} ` : `${applySqlKeywordCase("AS", keywordCase)} ${alias} `; +function formatAliasCompletionApply(alias: string): string { + return `${alias} `; } function generateAlias(tableName: string, existing = new Set()): string { @@ -5393,7 +5399,7 @@ function isOracleLikeDatabase(databaseType?: DatabaseType): boolean { return databaseType === "oracle" || databaseType === "oceanbase-oracle"; } -// CQL has no table alias syntax, so Cassandra must never receive `table AS alias` +// CQL has no table alias syntax, so Cassandra must never receive `table alias` // or standalone alias suggestions. function supportsTableAliases(databaseType?: DatabaseType): boolean { return databaseType !== "cassandra"; diff --git a/crates/dbx-core/tests/sql_analysis.rs b/crates/dbx-core/tests/sql_analysis.rs index b03d8f05b0..2e397b6a93 100644 --- a/crates/dbx-core/tests/sql_analysis.rs +++ b/crates/dbx-core/tests/sql_analysis.rs @@ -799,3 +799,62 @@ fn generic_dialect_still_rejects_spark_datasource_clauses() { assert!(error.contains("USING")); } + +#[test] +fn oracle_admin_ddl_statements_do_not_raise_syntax_errors() { + for sql in [ + "create user dbx_tmp identified by \"pw\" account unlock;", + "alter user dbx_tmp identified by \"pw\";", + "alter user dbx_tmp default tablespace dbx_ts;", + "create tablespace dbx_ts datafile '/tmp/dbx_ts.dbf' size 10m autoextend on next 1m maxsize unlimited;", + "alter tablespace dbx_ts add datafile '/tmp/dbx_ts2.dbf' size 5m;", + "drop tablespace dbx_ts including contents and datafiles;", + "create profile dbx_prof limit failed_login_attempts 5;", + "create directory dbx_dir as '/tmp/dbx_dir';", + "create or replace public synonym dbx_syn for dual;", + "alter session set nls_date_format='YYYY-MM-DD';", + "grant create session to dbx_tmp;", + "revoke create session from dbx_tmp;", + ] { + let analysis = analyze_sql_references(sql, Some("oracle")) + .unwrap_or_else(|error| panic!("Oracle admin DDL should analyze: {error} ({sql})")); + assert!(analysis.tables.is_empty(), "unexpected table references for {sql}"); + assert!(analysis.columns.is_empty(), "unexpected column references for {sql}"); + } +} + +#[test] +fn oracle_admin_ddl_masking_keeps_following_statement_spans() { + let sql = + "select 1 from users;\ncreate user dbx_tmp identified by \"pw\" account unlock;\nselect u.id from users u;"; + + let analysis = analyze_sql_references(sql, Some("oracle")) + .unwrap_or_else(|error| panic!("masked Oracle admin DDL should analyze: {error}")); + + let tables: Vec<_> = analysis.tables.iter().map(|table| (table.name.as_str(), table.scope_id)).collect(); + assert_eq!(tables, vec![("users", 0), ("users", 1)]); + // The trailing statement keeps the exact line/column it had in the unmasked script + // ("users" starts after `select u.id from ` on line 3). + assert_eq!(analysis.tables[1].span.start_line, 3); + assert_eq!(analysis.tables[1].span.start_column, 18); + + let columns: Vec<_> = analysis.columns.iter().map(|column| column.name.as_str()).collect(); + assert_eq!(columns, vec!["id"]); + assert_eq!(analysis.columns[0].span.start_line, 3); +} + +#[test] +fn oracle_admin_ddl_tolerance_is_scoped_to_oracle_compatible_dialects() { + for dialect in [Some("generic"), Some("postgres"), Some("mysql"), None] { + let error = analyze_sql_references("create user dbx_tmp identified by \"pw\";", dialect) + .expect_err("non Oracle dialects must keep the parser error"); + assert!(!error.is_empty()); + } +} + +#[test] +fn oracle_syntax_errors_are_still_reported() { + let error = analyze_sql_references("select from where order;", Some("oracle")) + .expect_err("a real Oracle syntax error must still be reported"); + assert!(error.contains("sql parser error"), "unexpected error: {error}"); +} diff --git a/crates/dbx-sql/src/sql.rs b/crates/dbx-sql/src/sql.rs index 7bee198aa0..60c58feea7 100644 --- a/crates/dbx-sql/src/sql.rs +++ b/crates/dbx-sql/src/sql.rs @@ -954,6 +954,13 @@ fn split_sql_statement_ranges(sql: &str) -> Vec { split_sql_statement_ranges_with_options(sql, SqlParsingOptions::default()) } +/// Statement ranges that keep their byte offsets into `sql`, so callers can rewrite +/// individual statements in place (see the Oracle administrative DDL tolerance in +/// `sql_analysis`). +pub(crate) fn statement_ranges_for_database(sql: &str, db_type: DatabaseType) -> Vec { + split_sql_statement_ranges_with_options(sql, SqlParsingOptions::for_database_type(db_type)) +} + fn split_sql_statement_ranges_with_options(sql: &str, options: SqlParsingOptions) -> Vec { let mut ranges = Vec::new(); let mut start = 0; diff --git a/crates/dbx-sql/src/sql_analysis.rs b/crates/dbx-sql/src/sql_analysis.rs index 112a48f2a3..c81837468c 100644 --- a/crates/dbx-sql/src/sql_analysis.rs +++ b/crates/dbx-sql/src/sql_analysis.rs @@ -16,7 +16,10 @@ use sqlparser::keywords::Keyword; use sqlparser::parser::{Parser, ParserError}; use sqlparser::tokenizer::{Span, Token, TokenWithSpan, Tokenizer}; -use crate::sql::{starts_with_duckdb_result_sql_keyword, starts_with_executable_sql_keyword}; +use crate::models::connection::DatabaseType; +use crate::sql::{ + starts_with_duckdb_result_sql_keyword, starts_with_executable_sql_keyword, statement_ranges_for_database, +}; static CLICKHOUSE_STRICTNESS_FIRST_JOIN_RE: LazyLock = LazyLock::new(|| { Regex::new(r"(?i)\b(ANY|ALL|SEMI|ANTI|ASOF)\s+(LEFT|RIGHT|FULL|INNER|CROSS)(\s+OUTER)?\s+JOIN\b") @@ -26,6 +29,17 @@ static POSTGRES_DEFAULT_PRIVILEGES_RE: LazyLock = LazyLock::new(|| { Regex::new(r"(?i)^\s*ALTER\s+DEFAULT\s+PRIVILEGES\b").expect("valid PostgreSQL default privileges regex") }); +/// Oracle administrative DDL that sqlparser has no grammar for: Oracle and the +/// Oracle-compatible engines (OceanBase Oracle mode, Dameng) accept these statements, but +/// the analyzer routes them through sqlparser's generic dialect, which reports a bogus +/// `sql parser error` on statements the server happily runs (issue #9104). +static ORACLE_ADMIN_DDL_RE: LazyLock = LazyLock::new(|| { + Regex::new( + r"(?is)^\s*(?:(?:create|alter|drop)\s+(?:or\s+replace\s+)?(?:public\s+)?(?:user|tablespace|profile|directory|synonym|role|session|system)\b|(?:grant|revoke)\b)", + ) + .expect("valid Oracle administrative DDL regex") +}); + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SqlReferenceAnalysis { pub tables: Vec, @@ -123,7 +137,10 @@ pub fn analyze_sql_references(sql: &str, dialect: Option<&str>) -> Result return Err(error.to_string()), + Err(error) => match oracle_admin_ddl_gap_statements(dialect, &parser_sql) { + Some(statements) => statements, + None => return Err(error.to_string()), + }, }; let mut analyzer = Analyzer { @@ -793,6 +810,53 @@ fn starts_with_duckdb_parser_gap_sql(sql: &str) -> bool { && starts_with_executable_sql_keyword(sql, &["FROM", "SUMMARIZE", "SUMMARISE", "PIVOT", "UNPIVOT"]) } +/// Oracle-compatible dialects whose statements the analyzer parses with sqlparser's +/// generic dialect. Oracle connections report the PL/SQL formatter id (`oracle`) as +/// their analysis dialect, and Dameng follows the same Oracle-compatible grammar. +fn is_oracle_compatible_analysis_dialect(dialect: Option<&str>) -> bool { + let Some(name) = dialect else { + return false; + }; + let name = name.trim().to_ascii_lowercase(); + matches!(name.as_str(), "oracle" | "dameng") +} + +/// Re-parses `sql` with every Oracle administrative DDL statement blanked out, so the +/// statements around it keep their diagnostics instead of the whole script failing on a +/// false syntax error. Returns `None` when nothing matched or when the rest of the script +/// still does not parse, in which case the caller keeps the original parser error. +fn oracle_admin_ddl_gap_statements(dialect: Option<&str>, sql: &str) -> Option> { + if !is_oracle_compatible_analysis_dialect(dialect) { + return None; + } + let mut masked: Option = None; + for range in statement_ranges_for_database(sql, DatabaseType::Oracle) { + if !ORACLE_ADMIN_DDL_RE.is_match(&range.text) { + continue; + } + let target = masked.get_or_insert_with(|| sql.to_string()); + let blanked = blank_out_statement_text(target.get(range.start..range.end)?); + target.replace_range(range.start..range.end, &blanked); + } + Parser::parse_sql(&GenericDialect {}, masked.as_deref()?).ok() +} + +/// Replaces every character of a statement with a space while keeping line breaks and the +/// original byte length, so token positions of the statements that follow stay exact. +fn blank_out_statement_text(statement: &str) -> String { + let mut blanked = String::with_capacity(statement.len()); + for character in statement.chars() { + if character == '\n' || character == '\r' { + blanked.push(character); + } else { + for _ in 0..character.len_utf8() { + blanked.push(' '); + } + } + } + blanked +} + fn starts_with_postgres_parser_gap_sql(sql: &str) -> bool { POSTGRES_DEFAULT_PRIVILEGES_RE.is_match(sql) } diff --git a/docs/screenshots/issue-9104-oracle-admin-ddl-no-syntax-error.png b/docs/screenshots/issue-9104-oracle-admin-ddl-no-syntax-error.png new file mode 100644 index 0000000000000000000000000000000000000000..e9625d6b9b0dc748e9ebda942bd728b418efbab9 GIT binary patch literal 36965 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 Lu6{1-oD!M<_d=Ro literal 0 HcmV?d00001 diff --git a/docs/screenshots/issue-9525-table-alias-implicit-form.png b/docs/screenshots/issue-9525-table-alias-implicit-form.png new file mode 100644 index 0000000000000000000000000000000000000000..fed4c82f4c9ba33bbf5ca0b2b5579d7ee74bf976 GIT binary patch literal 47186 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

lEbG#Ooz7GHw)n~d*dG(0cnRiDf0o1vsUY)che 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 literal 0 HcmV?d00001 diff --git a/packages/app-tests/sqlCompletion.test.ts b/packages/app-tests/sqlCompletion.test.ts index 76f3d52c4b..3490ae594b 100644 --- a/packages/app-tests/sqlCompletion.test.ts +++ b/packages/app-tests/sqlCompletion.test.ts @@ -206,14 +206,8 @@ test("suggests database-specific data types and functions", () => { assert.ok(mysqlCurrentTimestampItems.some((item) => item.type === "function" && item.label === "CURRENT_TIME")); assert.ok(mysqlCurdateItems.some((item) => item.type === "function" && item.label === "CURDATE")); assert.ok(mysqlIfnullItems.some((item) => item.type === "function" && item.label === "IFNULL")); - assert.equal( - mysqlDateAddItems.find((item) => item.type === "function" && item.label === "DATE_ADD")?.apply, - "DATE_ADD(${date}, INTERVAL ${expr} ${unit})", - ); - assert.equal( - mysqlDateSubItems.find((item) => item.type === "function" && item.label === "DATE_SUB")?.apply, - "DATE_SUB(${date}, INTERVAL ${expr} ${unit})", - ); + assert.equal(mysqlDateAddItems.find((item) => item.type === "function" && item.label === "DATE_ADD")?.apply, "DATE_ADD(${date}, INTERVAL ${expr} ${unit})"); + assert.equal(mysqlDateSubItems.find((item) => item.type === "function" && item.label === "DATE_SUB")?.apply, "DATE_SUB(${date}, INTERVAL ${expr} ${unit})"); assert.ok(mysqlSubstringIndexItems.some((item) => item.type === "function" && item.label === "SUBSTRING_INDEX")); assert.ok(mysqlLeftItems.some((item) => item.type === "function" && item.label === "LEFT")); assert.ok(mysqlLeftItems.some((item) => item.type === "keyword" && item.label === "LEFT")); @@ -233,9 +227,15 @@ test("suggests database-specific data types and functions", () => { databaseType: "mysql", }); assert.equal(mysqlDateTypeItems[0]?.label, "DATE"); - assert.equal(mysqlDateTypeItems.some((item) => item.type === "function" && item.label === "DATE"), false); + assert.equal( + mysqlDateTypeItems.some((item) => item.type === "function" && item.label === "DATE"), + false, + ); assert.equal(mysqlTimeTypeItems[0]?.label, "TIME"); - assert.equal(mysqlTimeTypeItems.some((item) => item.type === "function" && item.label === "TIME"), false); + assert.equal( + mysqlTimeTypeItems.some((item) => item.type === "function" && item.label === "TIME"), + false, + ); const mysqlCreateViewItems = buildSqlCompletionItems("CREATE VIEW v AS SELECT dat", "CREATE VIEW v AS SELECT dat".length, { tables: [], @@ -1397,7 +1397,10 @@ test("does not mix routines into an explicit table alias column completion", () }); assert.ok(items.some((item) => item.label === "name" && item.type === "column")); - assert.equal(items.some((item) => item.label === "name_formatter"), false); + assert.equal( + items.some((item) => item.label === "name_formatter"), + false, + ); }); test("suggests matching database functions alongside referenced columns", () => { @@ -2194,15 +2197,7 @@ test("extracts JOIN tables without explicit aliases", () => { }); test("extracts MySQL backtick-qualified tables across a JOIN", () => { - const sql = [ - "select", - " `jobdb`.`job_application_ats_process`.`process_id`,", - " count(*)", - "from", - " `jobdb`.`job_application`", - "join `jobdb`.`job_application_ats_process` on", - " `jobdb`.`job_application`.`id` = `jobdb`.`job_application_ats_process`.`app_id`", - ].join("\n"); + const sql = ["select", " `jobdb`.`job_application_ats_process`.`process_id`,", " count(*)", "from", " `jobdb`.`job_application`", "join `jobdb`.`job_application_ats_process` on", " `jobdb`.`job_application`.`id` = `jobdb`.`job_application_ats_process`.`app_id`"].join("\n"); const context = getSqlCompletionContext(sql, sql.length); assert.deepEqual( @@ -2302,10 +2297,7 @@ test("preserves an exact routine match before truncating candidates", () => { const sql = "select aaa"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [], - objects: [ - ...Array.from({ length: 200 }, (_, index) => ({ name: `a_a_a_${index}`, type: "procedure" as const })), - { name: "aaa", type: "function" }, - ], + objects: [...Array.from({ length: 200 }, (_, index) => ({ name: `a_a_a_${index}`, type: "procedure" as const })), { name: "aaa", type: "function" }], columnsByTable: new Map(), }); @@ -3517,7 +3509,10 @@ test("uses row-source aliases for all columns across multiple tables", () => { columns.some((item) => item.label === "o.id" && item.apply === "o.id"), "should show o.id", ); - assert.ok(columns.some((item) => item.label === "u.name" && item.apply === "u.name"), "unique name should use its row-source alias"); + assert.ok( + columns.some((item) => item.label === "u.name" && item.apply === "u.name"), + "unique name should use its row-source alias", + ); assert.ok( columns.some((item) => item.label === "o.user_id" && item.apply === "o.user_id"), "unique user_id should use its row-source alias", @@ -3574,7 +3569,8 @@ test("suggests table alias after FROM table", () => { }); const aliasItem = items.find((item) => item.type === "snippet" && item.detail?.includes("alias for")); assert.ok(aliasItem, "should suggest alias for table"); - assert.ok(aliasItem!.apply!.includes("AS"), "alias apply should include AS"); + assert.equal(aliasItem!.apply, "us "); + assert.ok(!aliasItem!.apply!.includes("AS"), "alias apply should use the implicit form"); }); test("applies the configured keyword case to generated table aliases", () => { @@ -3585,7 +3581,7 @@ test("applies the configured keyword case to generated table aliases", () => { keywordCase: "lower", }); const tableItem = tableItems.find((item) => item.type === "table" && item.label === "orders"); - assert.equal(tableItem?.apply, "orders as ord"); + assert.equal(tableItem?.apply, "orders ord"); const aliasItems = buildSqlCompletionItems("select * from orders ", "select * from orders ".length, { tables, @@ -3593,7 +3589,7 @@ test("applies the configured keyword case to generated table aliases", () => { keywordCase: "lower", }); const aliasItem = aliasItems.find((item) => item.type === "snippet" && item.detail === "alias for orders"); - assert.equal(aliasItem?.apply, "as ord "); + assert.equal(aliasItem?.apply, "ord "); }); test("keeps generated table aliases uppercase when keyword case is preserved", () => { @@ -3604,7 +3600,7 @@ test("keeps generated table aliases uppercase when keyword case is preserved", ( keywordCase: "preserve", }); const tableItem = items.find((item) => item.type === "table" && item.label === "orders"); - assert.equal(tableItem?.apply, "orders AS ord"); + assert.equal(tableItem?.apply, "orders ord"); }); test("prioritizes table acronym matches above alias snippets", () => { @@ -3617,7 +3613,7 @@ test("prioritizes table acronym matches above alias snippets", () => { assert.equal(items[0]?.label, "user_basic_info"); assert.equal(items[0]?.type, "table"); assert.ok( - items.some((item) => item.type === "snippet" && item.apply === "AS ubi "), + items.some((item) => item.type === "snippet" && item.apply === "ubi "), "alias snippet should remain available", ); }); @@ -3641,8 +3637,8 @@ test("table alias suggestions avoid reserved words", () => { const aliasItem = items.find((item) => item.type === "snippet" && item.detail === "alias for orders"); assert.ok(aliasItem); - assert.notEqual(aliasItem!.apply, "AS or "); - assert.equal(aliasItem!.apply, "AS ord "); + assert.notEqual(aliasItem!.apply, "or "); + assert.equal(aliasItem!.apply, "ord "); }); test("automatic table aliases avoid reserved words", () => { @@ -3654,17 +3650,17 @@ test("automatic table aliases avoid reserved words", () => { const tableItem = items.find((item) => item.type === "table" && item.label === "orders"); assert.ok(tableItem); - assert.notEqual(tableItem!.apply, "orders AS or"); - assert.equal(tableItem!.apply, "orders AS ord"); + assert.notEqual(tableItem!.apply, "orders or"); + assert.equal(tableItem!.apply, "orders ord"); }); test("automatic table aliases respect text after the cursor", () => { const cases: Array<[string, number, string]> = [ ["select * from ord AS o", "select * from ord".length, "orders"], ["select * from ord o", "select * from ord".length, "orders"], - ["select * from ord where id = 1", "select * from ord".length, "orders AS ord"], - ["select * from ord", "select * from ord".length, "orders AS ord"], - ["select * from ord, users", "select * from ord".length, "orders AS ord"], + ["select * from ord where id = 1", "select * from ord".length, "orders ord"], + ["select * from ord", "select * from ord".length, "orders ord"], + ["select * from ord, users", "select * from ord".length, "orders ord"], ["select * from orders AS o", "select * from or".length, "orders"], ["select * from ord单 AS o", "select * from ord".length, "orders"], ["select * from orde\u0301 AS o", "select * from ord".length, "orders"], @@ -3676,7 +3672,7 @@ test("automatic table aliases respect text after the cursor", () => { ["select * from ord -- comment\n o", "select * from ord".length, "orders"], ["select * from ord\n o", "select * from ord".length, "orders"], ["select * from ord /* ; */ AS o", "select * from ord".length, "orders"], - ["select * from ord /* comment */ where id = 1", "select * from ord".length, "orders AS ord"], + ["select * from ord /* comment */ where id = 1", "select * from ord".length, "orders ord"], ]; for (const [sql, cursor, expectedApply] of cases) { @@ -3719,26 +3715,26 @@ test("table alias suggestions avoid SQL keywords", () => { const aliasItem = items.find((item) => item.type === "snippet" && item.detail === "alias for item_file"); assert.ok(aliasItem); - assert.notEqual(aliasItem!.apply, "AS if "); - assert.equal(aliasItem!.apply, "AS it "); + assert.notEqual(aliasItem!.apply, "if "); + assert.equal(aliasItem!.apply, "it "); }); test("automatic table aliases avoid SQL keywords", () => { const cases: Array<[string, string]> = [ - ["account_store", "account_store AS ac"], - ["account_type", "account_type AS ac"], - ["data_order", "data_order AS da"], - ["invoice_note", "invoice_note AS inv"], - ["item_file", "item_file AS it"], - ["item_status", "item_status AS it"], - ["new_order", "new_order AS ne"], - ["no_config", "no_config AS nc"], - ["order_node", "order_node AS ord"], - ["order_flow", "order_flow AS ord"], - ["order_region", "order_region AS ord"], - ["row_value", "row_value AS rv"], - ["use_case", "use_case AS uc"], - ["user_role", "user_role AS ur"], + ["account_store", "account_store ac"], + ["account_type", "account_type ac"], + ["data_order", "data_order da"], + ["invoice_note", "invoice_note inv"], + ["item_file", "item_file it"], + ["item_status", "item_status it"], + ["new_order", "new_order ne"], + ["no_config", "no_config nc"], + ["order_node", "order_node ord"], + ["order_flow", "order_flow ord"], + ["order_region", "order_region ord"], + ["row_value", "row_value rv"], + ["use_case", "use_case uc"], + ["user_role", "user_role ur"], ]; for (const [tableName, expectedApply] of cases) { @@ -3776,8 +3772,8 @@ test("table alias suggestions avoid existing aliases", () => { const aliasItem = items.find((item) => item.type === "snippet" && item.detail === "alias for customer_orders"); assert.ok(aliasItem); - assert.notEqual(aliasItem!.apply, "AS co "); - assert.equal(aliasItem!.apply, "AS cu "); + assert.notEqual(aliasItem!.apply, "co "); + assert.equal(aliasItem!.apply, "cu "); }); // --- CASE snippet --- @@ -3988,9 +3984,7 @@ test("boosts foreign-key related table candidates in JOIN table context", () => }); test("keeps automatic SQL Server aliases on foreign-key related JOIN candidates", () => { - const foreignKeysByTable = new Map([ - ["dbo.orders", [{ name: "orders_customer_id_fkey", column: "customer_id", ref_schema: "dbo", ref_table: "customers", ref_column: "id" }]], - ]); + const foreignKeysByTable = new Map([["dbo.orders", [{ name: "orders_customer_id_fkey", column: "customer_id", ref_schema: "dbo", ref_table: "customers", ref_column: "id" }]]]); const sql = "select * from dbo.orders o join cus"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [ @@ -4006,13 +4000,11 @@ test("keeps automatic SQL Server aliases on foreign-key related JOIN candidates" assert.equal(items[0]?.label, "customers"); assert.ok(items[0]?.detail?.includes("related by")); - assert.equal(items[0]?.apply, "customers AS cs"); + assert.equal(items[0]?.apply, "customers cs"); }); test("does not add aliases to foreign-key related JOIN candidates when disabled", () => { - const foreignKeysByTable = new Map([ - ["dbo.orders", [{ name: "orders_customer_id_fkey", column: "customer_id", ref_schema: "dbo", ref_table: "customers", ref_column: "id" }]], - ]); + const foreignKeysByTable = new Map([["dbo.orders", [{ name: "orders_customer_id_fkey", column: "customer_id", ref_schema: "dbo", ref_table: "customers", ref_column: "id" }]]]); const sql = "select * from dbo.orders o join cus"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [ @@ -4032,9 +4024,7 @@ test("does not add aliases to foreign-key related JOIN candidates when disabled" }); test("schema-qualifies foreign-key related JOIN candidates when the target table name spans schemas", () => { - const foreignKeysByTable = new Map([ - ["dbo.orders", [{ name: "orders_customer_id_fkey", column: "customer_id", ref_schema: "sales", ref_table: "customers", ref_column: "id" }]], - ]); + const foreignKeysByTable = new Map([["dbo.orders", [{ name: "orders_customer_id_fkey", column: "customer_id", ref_schema: "sales", ref_table: "customers", ref_column: "id" }]]]); const sql = "select * from dbo.orders o join cus"; const items = buildSqlCompletionItems(sql, sql.length, { tables: [ @@ -4053,14 +4043,11 @@ test("schema-qualifies foreign-key related JOIN candidates when the target table assert.ok(fkCandidate, "should surface the foreign-key related candidate"); // customers exists in both dbo and sales, so the FK candidate must qualify with // the referenced schema (sales.customers) instead of a bare, ambiguous customers. - assert.equal(fkCandidate?.apply, "sales.customers AS cs"); + assert.equal(fkCandidate?.apply, "sales.customers cs"); assert.equal(fkCandidate?.dedupeKey, "sales.customers"); // The FK candidate (higher boost) should win dedupe against the regular - // sales.customers candidate, leaving no bare `customers AS cs` entry. - assert.ok( - !items.some((item) => item.type === "table" && item.apply === "customers AS cs"), - "should not emit a bare unqualified customers candidate alongside the qualified FK candidate", - ); + // sales.customers candidate, leaving no bare `customers cs` entry. + assert.ok(!items.some((item) => item.type === "table" && item.apply === "customers cs"), "should not emit a bare unqualified customers candidate alongside the qualified FK candidate"); }); test("boosts inbound foreign-key table candidates in JOIN table context", () => {