8000 fix(sqlite): type a value of one of several types as unknown by colinhacks · Pull Request #49 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

fix(sqlite): type a value of one of several types as unknown - #49

Merged
colinhacks merged 3 commits into
mainfrom
sqlite-several-types
Oct 23, 2026
Merged

colinhacks merged 3 commits into
mainfrom
sqlite-several-types

Conversation

@colinhacks
Copy link
Copy Markdown
Owner

SQLite hands back one of several values from a compound's column, CASE, coalesce, ifnull, iif, a min or max of several arguments, and lag or lead with a default. The engine typed such a value by the values it knew and skipped one of no known type, so these were typed integer though they can return text, on STRICT tables too:

select n from t union all select json_extract(doc, '$.a') from t
select coalesce(s, x) from t, u          -- u.x declared without a type
select lead(n, 1, 'z') over (order by id) from t
select n from t union all select 'x'     -- the leftmost arm's declared type won
  • A value of no known type among them now makes the result unknown, unless it is a parameter, which still takes the others' type (select n from t union all select :p keeps :p an integer). A NULL literal still says nothing of the type.
  • A compound's column no longer falls back to the declared type SQLite reports for it, which is the leftmost arm's.
  • lag and lead with a default take the type they share with the default.
  • oracle/cases/sqlite_mixed_values holds each case over STRICT tables and an untyped column, every statement run; the engine's test a_value_of_one_of_several_types_is_unknown covers the parameter and NULL arms.
  • The SQLite page's Functions section says so.

packages/sqlite/engine.wasm is rebuilt.

Locally the full oracle with --strict-coverage, the engine tests, the loader's tests, the labs and the examples pass, except the loader test and the driver-matrix row for JSON built inside a scalar subquery: they fail the same way on main once the machine's SQLite is 3.54.1, which no longer passes JSON's subtype through a scalar subquery. That is separate from this change.

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

Two paths still give an integer type to values that can be text. In each one the PR claims the case is handled. Both were reproduced with this PR's rebuilt packages/sqlite/engine.wasm.

Reviewed changes

Reviewed the whole PR: a new rule for how SQLite types a value chosen from one of several, with its oracle case, unit test, docs bullet and rebuilt artifact.

  • values_type: the result is unknown when any non-NULL value that is not a parameter has no known type. Otherwise it is shared over the non-NULL values.
  • Compound columns: select_column now uses values_type. columns() no longer falls back to the declared type for a top-level compound.
  • lag/lead with a default: the column is typed over the first argument and the default.
  • Tests and docs: there is a new oracle case sqlite_mixed_values, a new engine test, and a new bullet on the SQLite page. tests/fingerprint.test.ts passes on the rebuilt engine.wasm.

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

Comment thread engine/sqlite/src/sqlite.rs Resolved
Comment thread engine/sqlite/src/sqlite.rs Resolved
@colinhacks
colinhacks force-pushed the sqlite-several-types branch 2 times, most recently from 034772d to 724b0be Compare October 22, 2026 23:58
@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

Both earlier findings are fixed. The new argument() arm covers only the default, though, so a parameter given as lag/lead's first argument is now typed integer for its column while the parameter itself stays untyped. On main that column was unknown.

Reviewed changes

Reviewed 724b0be, which landed after the prior review.

  • Declared-type fallback removed: columns() no longer falls back to SQLite's declared type for any column. I ran the same statements on main and on the PR's engine.wasm: table columns, subqueries, views, FTS5, generated columns, WITHOUT ROWID, json_each, pragma_table_info, CTEs and unions. Only the intended compound columns changed. tests/fingerprint.test.ts passes.
  • lag/lead default parameter: argument() gives a parameter in argument 2 the first argument's type.
  • Tests: the engine test now covers the subquery, view and WITH compounds and lag(n, 1, :p). The sqlite_mixed_values oracle case has matching statements.

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

Comment thread engine/sqlite/src/sqlite.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.

Reviewed changes

Reviewed 952342e, which fixes the last open finding.

  • Symmetric lag/lead parameter typing: when there are three or more arguments, a parameter in argument() takes the type of the other value argument (n.args[2 - i]), so the value and the default each get the other's type. This matches what values_type assumes for the column, so lag(:p, 1, n) now types both p and v as integer. Like others(), the arm gives None when the other argument's type is unknown.
  • Tests: the engine test loops over both parameter positions and checks the column and the parameter. The oracle case adds lag_parameter_value.sql, blessed as (p: integer) -> { v: integer? }. tests/fingerprint.test.ts passes on the committed packages/sqlite/engine.wasm.

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

A compound's column, CASE, coalesce, ifnull, iif, min and max of several
arguments, and lag and lead with a default skipped an argument of no known
type, so a union of an integer column and json_extract was typed integer.
Such an argument now makes the value unknown, except a parameter, which
takes the others' type. A compound's column no longer falls back to the
leftmost arm's declared type, and lag and lead read their default.
SQLite names a compound's declared type by one arm, so a compound read
through a subquery, view or WITH query took it. Nothing else needed the
declared-type fallback, so it is gone. A parameter as lag's or lead's
default now takes the first argument's type.
@colinhacks
colinhacks force-pushed the sqlite-several-types branch from 952342e to 3c8eae5 Compare October 23, 2026 00:25
@colinhacks
colinhacks merged commit 0a3b121 into main Oct 23, 2026
6 checks passed
@colinhacks
colinhacks deleted the sqlite-several-types branch October 23, 2026 00:54
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