8000 fix(sqlite): treat % by a fractional literal under 1 as nullable by fengqiaoqu · Pull Request #124 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

fix(sqlite): treat % by a fractional literal under 1 as nullable - #124

Open
fengqiaoqu wants to merge 1 commit into
colinhacks:mainfrom
fengqiaoqu:fix/sqlite-modulo-fractional-divisor
Open

fengqiaoqu wants to merge 1 commit into
colinhacks:mainfrom
fengqiaoqu:fix/sqlite-modulo-fractional-divisor

Conversation

@fengqiaoqu
Copy link
Copy Markdown

Fixes #122.

SQLite casts both operands of % to INTEGER before the remainder, so a literal such as 0.5 truncates to 0 and every row is NULL. / and % shared one non-zero literal rule; that is right for / and wrong for %.

Statement Before After
select qty % 0.5 as r from items r: real r: real?
select qty % 2.5 as r from items r: real r: real
select qty / 0.5 as r from items r: real r: real

packages/sqlite/engine.wasm is rebuilt. Oracle case sqlite_modulo_divisor and the engine unit test cover it; the SQLite docs page says so.

SQLite casts both operands of % to INTEGER before the remainder, so a
literal such as 0.5 truncates to 0 and the result is NULL on every row.
The engine shared / and %'s non-zero literal rule, which is correct for
division but not for modulo.

Fixes colinhacks#122
@vercel
vercel Bot commented Oct 26, 2026
Copy link
Copy Markdown

@fengqiaoqu is attempting to deploy a commit to the pullfrog Team on Vercel.

A member of the Team first needs to authorize it.

@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 issues with the engine rule — one docs wording nit inline.

Reviewed changes

Reviewed the whole PR: the SQLite engine's rule for the / and % divisor, its tests, the rebuilt artifact and the docs.

  • Divisor rule split by operator: in type_of, % now treats a literal divisor as non-zero only when it truncates to a non-zero integer. / keeps the old v != 0.0 check.
  • Tests: a new engine unit test, plus the oracle case sqlite_modulo_divisor with six statements that cover % 0.5, % 0.99, % 1e-3, % 2.5, % 2 and / 0.5.
  • Artifact: packages/sqlite/engine.wasm is rebuilt. tests/fingerprint.test.ts passes for SQLite on this head, so the artifact matches its sources.
  • Docs: the Expressions paragraph in sqlite.mdx now states the % case.

I checked the rule against SQLite itself using Node 24's node:sqlite. qty % 0.5, % 0.99, % 1e-3, % -0.5 and % 0x0 return NULL on every row. % 2.5 returns a non-null real, and so does % 1e400, which clamps to the largest int64. / 0.5 is non-null. In each case the engine gives the same answer. A negative literal parses as a neg node, so it already falls through to nullable. The other % sites, nulls_with and the parameter context at L2008, don't depend on what the divisor's value is.

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

**Expressions**

Reylo's table of SQLite's functions says which are non-null over non-null inputs: `length`, `coalesce`, `||` and arithmetic are; division by a column is not, since dividing by zero returns `NULL`. Arithmetic on a `REAL` with no answer is the one exception ([Limits](#limits)).
Reylo's table of SQLite's functions says which are non-null over non-null inputs: `length`, `coalesce`, `||` and arithmetic are; division by a column is not, since dividing by zero returns `NULL`. `%` by a fractional literal under 1 is not either: SQLite casts both operands to INTEGER first, so `0.5` truncates to `0` and the remainder is `NULL` on every row. Arithmetic on a `REAL` with no answer is the one exception ([Limits](#limits)).
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.

The new sentence starts with inline code (`%` by a fractional literal…). AGENTS.md's writing conventions say docs prose never opens a sentence with inline code. Rewording it to start with "Nor is" keeps the meaning:

Suggested change
Reylo's table of SQLite's functions says which are non-null over non-null inputs: `length`, `coalesce`, `||` and arithmetic are; division by a column is not, since dividing by zero returns `NULL`. `%` by a fractional literal under 1 is not either: SQLite casts both operands to INTEGER first, so `0.5` truncates to `0` and the remainder is `NULL` on every row. Arithmetic on a `REAL` with no answer is the one exception ([Limits](#limits)).
Reylo's table of SQLite's functions says which are non-null over non-null inputs: `length`, `coalesce`, `||` and arithmetic are; division by a column is not, since dividing by zero returns `NULL`. Nor is `%` by a fractional literal under 1: SQLite casts both operands to INTEGER first, so `0.5` truncates to `0` and the remainder is `NULL` on every row. Arithmetic on a `REAL` with no answer is the one exception ([Limits](#limits)).

This branch has not been deployed

No deployments
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.

sqlite: x % 0.5 is typed non-null but SQLite returns NULL on every row

1 participant

0