8000 feat(engine): refuse a parameter as a whole sort or group key by colinhacks · Pull Request #42 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

feat(engine): refuse a parameter as a whole sort or group key - #42

Merged
colinhacks merged 2 commits into
mainfrom
param-sort-key
Oct 23, 2026
Merged

colinhacks merged 2 commits into
mainfrom
param-sort-key

Conversation

@colinhacks
@colinhacks colinhacks commented Oct 22, 2026 •
Copy link
Copy Markdown
Owner

A parameter is a value, the same for every row, so a parameter that is a whole order by key sorts nothing, and one that is a whole group by key folds every row into one group. The analysis accepted both silently, typing sort: string. It is now an error at the parameter, on Postgres, MySQL and SQLite.

Before: select id, name from users order by :sort::text typed cleanly, and the rows came back in storage order.

After: the type check reports, at :sort:

reylo17: order by :sort sorts every row by the same value, since a parameter is a value and not a column's name; choose the column with case :sort when 'name' then name end

group by :g reads "group by :g puts every row in one group, since …" (or "groups every row by the same value" beside another key), partition by :p "puts every row in one partition", and Postgres's distinct on (:d) "keeps a single row".

What counts as such a key: a bare parameter, or one under casts and collations (:sort, :sort::text, cast(:sort as text), convert(?, char), $1, ?, @s), as a key of a statement's order by or group by, a window's order by or partition by (inline or a named window), an aggregate's own order by (string_agg, group_concat), Postgres's distinct on, and MySQL's update/delete … order by, in subqueries, with queries, derived tables and set operations too. A key that holds a parameter stays allowed (case :sort when 'name' then name end, case when :desc then created_at end desc), and so do order by 2 and limit :n. Grouping sets (rollup(:g)) are not read.

  • Code 17, the next free one (14, 15 and 16 are taken). It follows code 13 (an on conflict target no key matches): the statement is still typed, its row and parameters as before, the error shows in the type check and fails reylo generate, and a build still emits the module.
  • Postgres reads it off libpg_query's tree and reports it beside whatever the server says.
  • SQLite reads SQLite's resolved tree. The serializer now carries a window call's partition by/order by and an aggregate's own order by. SQLite already refuses a parameter as a compound's order by term itself.
  • MySQL walks the sqlparser tree. The MySQL oracle runs a statement whose only diagnostics are code 17 rather than taking it as refused.
  • On MySQL and SQLite the error's span takes in a ::type written on the parameter (:sort::text), as every diagnostic at a cast parameter there does; Postgres's is the placeholder.

Verification

  • Engine tests in each dialect's crate.
  • Oracle cases param_keys, sqlite_param_keys and mysql_param_keys, blessed on Postgres 18 and MySQL 8.4; every statement there runs over the seeded values. mysql_types/param_order_by.sql now records the error.
  • A content mapper test, which checks the diagnostic, its place and the declaration that still types, and a loader test, which checks that the build still emits the module.
  • scripts/gates.sh cargo-build, engine-tests, oracle-tests, wasm, oracle, typecheck, loader-tests (0 skipped), package and site all pass.

A position or alias that SQLite resolves to a result column holding a parameter (select id, :tag as tag … order by 2) is not refused. Docs are not part of this PR: the "A parameter in place of a name" limit it was going to update has since been cut from the database pages.

Copilot AI balanced review requested due to automatic review settings October 22, 2026 22:05
@vercel
vercel Bot commented Oct 22, 2026 •
Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
reylo Ready Ready Preview Oct 23, 2026 4:34am UTC

Request Review

Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

On SQLite, a position or alias that names a result column which is a bare parameter is now refused as reylo14. The error lands in the select list and quotes a clause the author never wrote. Postgres and MySQL accept the same statement.

Reviewed changes

Reviewed both commits: the new reylo14 diagnostic across the three engines, the oracle and TS tests, and the docs.

  • reylo14 / CONSTANT_KEY: constant_key in engine/core builds a message for each clause (one wording when the key is alone, another beside other keys). Each dialect reports it at the placeholder and still types the statement.
  • Postgres: re-parses the rewritten text with libpg_query. It walks SortBy, group_clause, distinct_clause and the window partition_clause.
  • MySQL: walks the sqlparser AST, including update/delete … order by. It maps each ? back to the author's placeholder by line and column.
  • SQLite: the C serializer now emits a window call's partitionBy/orderBy and an aggregate's own orderBy. constant_keys walks the resolved tree.
  • MySQL oracle: a statement whose only diagnostics are code 14 is checked like any other, not counted as refused.
  • Oracle cases, tests, docs: new param_keys / sqlite_param_keys / mysql_param_keys cases, a mapper test and a loader test. The "A parameter in place of a name" limit is updated on every dialect page. The rebuilt engine.wasm files match their sources (tests/fingerprint.test.ts passes).

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread engine/sqlite/src/sqlite.rs Resolved
Comment thread engine/mysql/src/mysql.rs Resolved
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. Both earlier findings are fixed.

Reviewed changes

Since the last review, the branch was rebased onto main (a5427bf) and now has two commits: a4b51aa (the feature, without its wasm) and 6cf2886. This review covers 6cf2886 and what the rebase changed. git range-diff shows a4b51aa matches the previously reviewed 0234d8c apart from the wasm.

  • SQLite position/alias keys: added Walk::select_keys. It skips a select's own order by/group by key when the select list reads that parameter, because SQLite turns a position or alias into a copy of the result column. Narrowing the skip this way loses nothing: a parameter written twice is already reylo3, so a real order by :sort never shares its index with a select-list read. I ran the committed packages/sqlite/engine.wasm on several cases:
    • order by 2, order by tag, group by 1, 2 and order by tag collate nocase over a parameter column now give no diagnostics.
    • A bare order by :sort and a window's over (order by :w) are still reylo14.
  • MySQL doc comment: moved is_variable's doc comment back above fn is_variable.
  • Tests and wasm: added position and alias cases to the Postgres, MySQL and SQLite unit tests, and rebuilt all three engine.wasm. tests/fingerprint.test.ts passes.

ℹ️ PR description still says the docs changed

The rebase dropped the old docs commit (af1408d), because main's 3e697d2 removed the "A parameter in place of a name" limit from the database pages. The PR no longer touches site/, but its description still says that limit "now says the type check refuses such a key" on all three pages. Either edit that paragraph out of the description, or add the guidance back somewhere on the current pages.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks force-pushed the param-sort-key branch 3 times, most recently from 5767392 to f0520dd Compare October 23, 2026 00:02
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. The renumbering to reylo17 is complete and doesn't collide with any other code.

Reviewed changes

Since the last review, the branch was rebased onto main 094287e (#5). That commit took code 14 for duplicate column names, so this PR's diagnostic was renumbered.

  • CONSTANT_KEY 14 → 17: changed the constant in engine/core/src/analysis.rs, the filters in the Postgres, MySQL and SQLite engine tests, every expected.txt line in param_keys, sqlite_param_keys, mysql_param_keys and mysql_types, and the assertions and test name in tests/analyze.test.ts and tests/strict-params.test.ts. No reylo14 reference to this diagnostic is left. Codes 13, 14, 15 and 16 are all still in use on main, and 17 is free. The MySQL oracle's refusal filter reads CONSTANT_KEY rather than a hard-coded number, so it picks up the new code by itself.
  • Rebuilt artifacts: all three engine.wasm files match their sources at f0520dd (node --test tests/fingerprint.test.ts: 3 pass, 0 skipped).
  • PR description: the paragraph that still claimed the docs had changed is gone. It now says docs are out of scope, which settles the last review's only note.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks force-pushed the param-sort-key branch 4 times, most recently from 8d20760 to dddf278 Compare October 23, 2026 03:54
@colinhacks
colinhacks merged commit b6bf801 into main Oct 23, 2026
6 checks passed
@colinhacks
colinhacks deleted the param-sort-key branch October 23, 2026 04:41
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.

2 participants

0