Skip to content

fix: audit #24 — coalesce arity, MSSQL OUTPUT star, MySQL/SQLite JSONPath#89

Merged
productdevbook merged 2 commits into
mainfrom
fix/audit24
Apr 21, 2026
Merged

fix: audit #24 — coalesce arity, MSSQL OUTPUT star, MySQL/SQLite JSONPath#89
productdevbook merged 2 commits into
mainfrom
fix/audit24

Conversation

@productdevbook
Copy link
Copy Markdown
Owner

Summary

Three correctness issues from audit round #24:

  • HIGH — coalesce() silently accepted zero arguments (src/builder/eb.ts): every dialect rejects COALESCE(); the runtime driver error didn't point at the caller. Builder now throws.
  • HIGH — MSSQL OUTPUT emitted invalid three-part INSERTED.[tbl].* (src/printer/mssql.ts): returning(star("orders")) on MSSQL produced a reference SQL Server rejects at parse. The INSERTED/DELETED pseudo-tables cannot be qualified by a base-table name. Strip the qualifier; emit INSERTED.* / DELETED.*.
  • HIGH — MySQL / SQLite -> / ->> required JSONPath, not bare keys (src/printer/{mysql,sqlite}.ts): base printer emitted PG's bare-key form (data->'name'); MySQL rejects with ER_INVALID_JSON_PATH. Rewrite single-segment paths to $.name (or $[N] for numeric indices) with proper single-quote escaping.

Test plan

  • pnpm fmt && pnpm lint && pnpm typecheck clean
  • pnpm test — 1247 pass (+8 new)
  • Reproducers confirmed each bug pre-fix, verified fix post-patch
  • Updated audit5 MSSQL test expectations to match the new pseudo-table behavior
  • Regression test file: test/audit24-regressions.test.ts

🤖 Generated with Claude Code

productdevbook and others added 2 commits April 21, 2026 13:07
…Path

HIGH #1: coalesce() silently accepted zero arguments
  `COALESCE()` is invalid on every dialect (PG / MySQL / SQLite /
  MSSQL all reject). The variadic signature had no min-args check;
  the runtime driver error ("requires at least one argument") pointed
  at the DB instead of the caller. Added a builder-time throw.

HIGH #2: MSSQL OUTPUT emitted invalid three-part name
  `returning(star("orders"))` compiled to `OUTPUT INSERTED.[orders].*`
  on MSSQL — a three-part reference to a non-existent object. The
  INSERTED/DELETED pseudo-tables have all the target columns already
  and cannot be qualified by the base table name. Fix: strip the
  table qualifier when a star hits the MSSQL OUTPUT path; emit
  `INSERTED.*` / `DELETED.*`.

HIGH #4: MySQL / SQLite `->` / `->>` required JSONPath, not bare keys
  Both engines' JSON operators expect the RHS to be a JSONPath string
  starting with `$` (e.g. `data->'$.name'`, `data->'$[0]'`). The base
  printer emitted PG's bare-key form (`data->'name'`) which MySQL
  rejects with ER_INVALID_JSON_PATH and SQLite with "JSON path error".
  Overrode `printJsonAccess` on both MysqlPrinter and SqlitePrinter
  to rewrite single-segment paths to `$.name` / `$[N]`, with standard
  single-quote escaping of embedded quotes.

Updated audit5 MSSQL test expectations to match the new pseudo-table
behavior and added audit24 regression tests for each fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review of PR #89 noted that MySQL/SQLite's JSONPath rewrite treats
`at("a.b")` as a two-level path (`$.a.b`) while PG's printer treats
the same string as a literal single key. Add a docblock note
explaining the cross-dialect semantic scope: `.at(path)` takes a
single JSON selector string, and chained `.at("a").at("b")` is the
recommended portable form. Users who need path traversal on PG should
use `#>` with a segment array.

No behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@productdevbook productdevbook merged commit 9c9a85e into main Apr 21, 2026
1 check passed
@productdevbook productdevbook deleted the fix/audit24 branch April 21, 2026 10:10
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