8000 fix(sqlite): refuse a value its column's type does not allow by colinhacks · Pull Request #31 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

fix(sqlite): refuse a value its column's type does not allow - #31

Merged
colinhacks merged 4 commits into
mainfrom
sqlite-loose-values
Oct 23, 2026
Merged

colinhacks merged 4 commits into
mainfrom
sqlite-loose-values

Conversation

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

A SQLite table that is not STRICT stores a value of any type in any column, so an INTEGER column can hold the text 'abc'. Reylo types such a column by its declared type (number), but .run() returned the stored string as it was.

The run time now refuses a value whose storage class the column's type does not allow, with an error that names the column, as it already does for a BOOLEAN column holding 2:

Column "n" is typed number and holds the text "abc": outside a STRICT table SQLite stores a value of any type in any column, and Reylo refuses one its type does not allow rather than return it. Replace the value, or read what the column holds with cast(n as text).
  • Refused: text or a BLOB in a number or bigint column (a REAL with a fraction in a bigint one too), a BLOB in a string column, and anything but a BLOB in a Uint8Array column.
  • Kept: a NUMERIC column's text, any value in a column with no declared type (unknown), a REAL in an INTEGER column, and libSQL's integers under intMode: "string".
  • The message does not advise declaring the table STRICT, since a table with a BOOLEAN or BIGINT column cannot be one; the docs say a table whose columns a STRICT table takes can be.
  • The inferred types do not change. Widening every non-STRICT column's type would put a union on nearly every query of an existing database.
  • The check sits in the decoder every SQLite value already passes through, in the branches a value of another class takes, so a result whose values match their types decodes as fast as before (10,000 rows of six columns: the same time on main and on this branch, on better-sqlite3 and node:sqlite).
  • A value that is one of several kinds over STRICT tables (a union of an integer and text, lag with a text default) is typed unknown by fix(sqlite): type a value of one of several types as unknown #49, so it is not refused; a test checks a STRICT table's mixed union returns its rows.
  • Tests: a driver-free test in tests/values.test.ts, and the per-driver SQLite test in tests/representation.test.ts checks the refusal on better-sqlite3, node:sqlite, libSQL in every intMode, both Turso APIs, sqlite3 and the sqlite wrapper.
  • Docs: the SQLite page's "Declared types and STRICT tables" says so and its Limits link to it; the agent skill says it too.

Copilot AI balanced review requested due to automatic review settings October 22, 2026 20:58
@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:30am 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

The new refusal assumes a value of another class can only come from a table that is not STRICT. But two of the engine's typing rules can also hand plan() such a value, so some queries over STRICT tables that ran on main now throw, and the error tells the user to declare STRICT a table that already is.

Reviewed changes

Reviewed the single commit that makes the SQLite run time refuse a value whose storage class its column's kind does not allow.

  • refuseClass in plan(): text or a BLOB in an int/float/int8 column, a BLOB in a text column, and anything but a BLOB in a bytes column now throw a DecodeError that names the column. NUMERIC's text, unknown and libSQL's intMode "string" digits are kept.
  • int8 REALs: a REAL is refused in a bigint column, on exact drivers any number and on rounding drivers one with a fraction.
  • Docs: "Values Reylo refuses" gains the rule, and the data types intro, "Declared types and STRICT tables" and Limits link to it.
  • Tests: a driver-free test in tests/values.test.ts, which passes locally on Node 24, and per-driver refusals in tests/representation.test.ts, which I did not run since it needs lab/drivers.

I checked which kinds the SQLite engine emits. Mixed coalesce/iif/case arms become unknown through shared, so no json|… or bool|… union reaches the new branches. That part holds up.

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

Comment thread src/decode-sqlite.ts Resolved
Comment thread site/content/docs/sqlite.mdx 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 in the latest commit. One earlier thread is still open: the engine typing gap on STRICT tables.

Reviewed changes

Reviewed ab38045 and the rebase onto the rewritten SQLite docs.

  • Run-time message: refuseClass no longer tells the user to declare the table STRICT, so the int8 special case is gone. It still says "outside a STRICT table SQLite stores a value of any type", which is why the open thread on src/decode-sqlite.ts stays open. The union all and lag/lead cases on STRICT tables still throw with a message that blames a table that is not STRICT.
  • Docs: the refusal now sits in "Declared types and STRICT tables". The intro sentence names it, a new "A value of another kind" bullet explains it, and the Limits entry links to it and says "where you can". No links point to the removed #values-reylo-refuses anchor.
  • Tests: updated the tests/values.test.ts regexes to match the new message, including the trailing cast(… as text). I checked them by reading only. I didn't run them because the runner has no node_modules.

Pullfrog  | Fix it ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

A table that is not STRICT stores any value in any column, so an INTEGER
column can hold the text 'abc'. The run time returned it as it was, under
a number type; it now refuses it with an error naming the column, as it
does a BOOLEAN column's 2.
@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 STRICT-table concern from the first review is settled: #49 is in this branch's base, and the new test covers it.

Reviewed changes

Reviewed the one change since 1f85864: a regression test for a STRICT table's mixed union.

  • Mixed union over a STRICT table: tests/representation.test.ts adds a strict table tight and checks that select n as v from tight union all select 'x' returns [{ v: 5 }, { v: "x" }] on better-sqlite3 and node:sqlite, instead of throwing a DecodeError. This relies on #49 (0a3b121), which is in the base. #49 also types lag/lead with a default as unknown when the kinds mix (engine/sqlite/src/sqlite.rs:1252), so I resolved the earlier thread about false refusals.

ℹ️ The PR description still quotes the old error message

The PR body still quotes the earlier message, which ends "declare the table STRICT so that SQLite refuses another like it". It also still has the bullet saying a bigint column's error leaves that advice out. Since ab38045, no refusal message advises STRICT. If the body becomes the squash commit's message, update it so it matches refuseClass in src/decode-sqlite.ts.

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

@colinhacks
colinhacks merged commit 191203b into main Oct 23, 2026
6 checks passed
@colinhacks
colinhacks deleted the sqlite-loose-values branch October 23, 2026 04:38
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