Skip to content

Quote identifiers a database will not read back as itself - #25

Merged
rahulmahadik merged 4 commits into
mainfrom
develop
Aug 13, 2026
Merged

Quote identifiers a database will not read back as itself#25
rahulmahadik merged 4 commits into
mainfrom
develop

Conversation

@rahulmahadik

Copy link
Copy Markdown
Owner

A mixed-case Postgres schema failed every query. An unquoted name folds to lower case and resolves to nothing; Oracle folds the other way; MySQL on Linux compares table names case-sensitively. Table and column names are now quoted from the catalog before the statement is validated, and names that are already correct are left untouched.

Reserved words now come from each database rather than one shared guess, read through pg_get_keywords(), information_schema.KEYWORDS, V\$RESERVED_WORDS and duckdb_keywords(). MySQL reserves 262 words where the shared list had about a hundred. tools/generate-sql-keywords.mjs regenerates both the core and plugin copies and refuses to write if an engine is unreachable.

Rewriting SQL text can change what a query means, and the AST guard only checks safety, so the rewriter leaves alone anything that is syntax rather than a name: string literals including escaped apostrophes and dollar-quoted bodies, comments, CAST types, EXTRACT fields, typed literals such as TIMESTAMP '2024-01-01', schema qualifiers, and any name spelled two ways across the catalog.

Also included: nested aggregates repaired before they run, set operations no longer tripping the unknown-column floor, the prompt naming the connected database so system-catalog filters are real, and an unescaped apostrophe explained rather than looping until the attempts run out.

Verification

  • TypeScript 2641, Kotlin 647, zero failures; coverage 94.96% statements
  • Six-engine live baseline 33/33 on Postgres, MySQL, SQLite, DuckDB, Oracle and MongoDB
  • 24 adversarial schemas (reserved words, unicode, spaces, empty tables, SQL-looking data)
  • Five real open-source schemas: Pagila, Northwind, Sakila, Chinook on SQLite and Oracle
  • Models 3b, 7b and 30b

Ships JetBrains 0.5.2 and VS Code 0.7.1, plus a core patch.

A mixed-case Postgres schema failed every query: an unquoted name folds to
lower case and resolves to nothing, Oracle folds the other way, and MySQL on
Linux compares table names case-sensitively. Table and column names are now
quoted from the catalog before the statement is validated, and a name that is
already correct is left alone.

Reserved words come from each database rather than one shared guess, read
through pg_get_keywords(), information_schema.KEYWORDS, V$RESERVED_WORDS and
duckdb_keywords(). MySQL reserves 262 words where the guess had about a
hundred. tools/generate-sql-keywords.mjs regenerates both this and the plugin's
copy, and refuses to write if an engine is unreachable.

Rewriting SQL text can change what a query means, so the rewriter leaves alone
anything that is syntax rather than a name: string literals including escaped
apostrophes and dollar-quoted bodies, comments, CAST types, EXTRACT fields,
typed literals such as TIMESTAMP '2024-01-01', schema qualifiers, and a name
spelled two ways across the catalog.

Also: nested aggregates are repaired before they run, a set operation no longer
trips the unknown-column floor, the prompt names the connected database so
system-catalog filters are real, and an unescaped apostrophe is explained
instead of looping until the attempts run out.
The plugin carries its own Kotlin engine, so the same rules have to exist twice
and stay in step. The tests mirror the core suite case for case, which is what
catches a divergence: the nested-aggregate branch here never incremented the
repair counter, and the column floor read raw SQL where core reads text with
literals blanked, so a value containing the word union disabled it.
…st account

The screenshot was in no README or doc, so its stale disclaimer was never shown.
The certification note answers the Edge policy that asks for test credentials:
AskSQL has no accounts to issue, so it explains that and gives a reviewer a free
path in with a local model and any spreadsheet.
@rahulmahadik
rahulmahadik merged commit a9b987e into main Aug 13, 2026
23 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant