Repository navigation
fix(compiler): carry a U64 parameter above i64::MAX - #1102
Conversation
A parameter's literal carrier was a signed 64-bit integer, so a U64 parameter bound to 18446744073709551615 was refused at bind time and a U64 row above i64::MAX was unreachable. The carrier is now an i128, which holds every declared integer type with one spelling per value, and the value is narrowed only at a typed boundary, from the declared type. A saved plan keeps its i64 arm and carries a wider value as its decimal text, so existing saved plans still decode. Engine v1 narrows with i64::try_from and refuses other values, as its frozen semantics do not change; its pinned hashes move with it.
|
Do not merge this commit. The compiler crate does not build. The change is the right repair for issue 918 once the two WhyStorage, comparison, range checks, and scan pushdown already accept a full What this change does
A declared The saved-plan mirror keeps the v1 narrows with Blocking
Replace both calls with The previous impl used Optional
Leave
LiabilityOne v2 pushdown builds a typed DataFusion value ( Public The user guide and ValidationReviewed commit: Before, on that base, with the new case file present only for the run:
Result: failed, for the predicted reason. On the reviewed commit:
The official GQT case cannot run on the unmodified commit, because the crate does not build. I then applied a local-only
LimitationsI did not run the HTTP server or the CLI as separate processes. Both call Inline comments that would have been attached:
|
Rust has no `From<isize> for i128` and no `From<usize> for i128`, so the two `ToParam` implementations did not compile and the compiler crate could not build. The widening is exact, so cast instead.
|
Thanks — that was the whole build break, and you were right about the cause. Fixed in I confirmed the trait bounds with the local toolchain rather than from memory: so the other One limit to be explicit about: I could not run |
|
The compile break is fixed. The parameter change is sound. Do not merge yet. One documentation check is still red. WhyA declared Storage, comparison, range checks, and scan pushdown already accept a full What this change does
A declared The saved-plan mirror keeps the v1 narrows with Delta since 202153a
On this host, and on every target rustc 1.97 supports, The author said this machine could not run Earlier findings
Blocking
Check AGENTS.md Links failed on this head. The log says Shorten the page to 350 lines. Use the reviewed exemption marker only if a reviewer accepts a longer page. Optional
Leave LiabilityOne The new v2 pushdown builds Public A query-text integer is still ValidationReviewed commit: Before, on that base, with the new case file present only for the run:
Result: failed, for the predicted reason. On the reviewed commit, after a rebuild of this head:
CI on this head: Lint (clippy), Format, Test Workspace, GQT (ordinary), GQ Logic Tests, and DST pinned suite passed. Check AGENTS.md Links failed. CI Gate failed because of LimitationsI did not run the HTTP server or the CLI as separate processes. Both call Inline comment on This paragraph makes the page 354 lines. Inline comment on Optional. |
`scripts/check-docs.py` refuses an authored user page over 350 lines, and `docs/user/queries/index.md` was already exactly at the cap, so the new paragraph about a `U64` parameter's range pushed it to 354 and failed the Check AGENTS.md Links job. Each fact now sits in the paragraph that already covers its subject: the parameter's range joins the declarations paragraph, the decimal-string advice joins the JSON input paragraph, and the two pointer sentences that stood alone as their own paragraphs close the paragraph they belong with. No sentence is dropped. Also bump the saved-plan envelope to version 11. The `wide_integer` arm is a new wire form, and version 10 has to name one schema: an older reader that meets the arm now refuses on the version before it reaches the arm, and says so.
… fix/u64-param-over-i64-max
|
Thanks for the detailed review — the compile break is fixed and the parameter change is sound, and I have taken both remaining items. The documentation check. You were right that the paragraph broke the CI gate: Each fact now sits in the paragraph that already covers its subject, so nothing had to be cut:
Two pointer sentences that stood alone as their own paragraphs ("Use full-text search ..." and "See Branches, Commits, and History.") now close the paragraph they belong with. No sentence is dropped from the page — I checked the old page sentence by sentence against the new one, and the only two that differ are the ones this change moves. $ python scripts/check-docs.py
Documentation OK (386 Markdown files checked).The page is 349 lines, one under the cap. The plan version. You are right that version 10 has to name one schema. The The constant's doc comment records what version 11 adds, $ python scripts/release_notes.py preview --working-tree
...
- Saved query plans now use envelope version 11, which carries a `U64` parameter above `i64::MAX` as its decimal text. Regenerate plans saved with earlier binaries; older binaries reject version 11 plans.Validation. I did not build or run anything locally — CI on this pull request is the judge, as before. The documentation checks above are the two Python checks in this repository that need no toolchain, and both pass. The change adds no dependency and no new process or filesystem lifecycle. |
Closes #918.
The problem
A
U64parameter bound to a value abovei64::MAXwas refused at bind time:The literal carrier was a signed 64-bit integer, so every unsigned value above
i64::MAXwas rejected on the way in — aU64column holding18446744073709551615was unreachable, and a mutation parameter abovei64::MAXfailed the same way.The fix
The carrier is widened to
i128and the value is narrowed at a typed boundary, from the declared type:Literal::Integercarries ani128(crates/omnigraph-compiler/src/query/ast.rs), soI32,I64,U32andU64share the one spelling and no value is lost on the way in.crates/omnigraph-compiler/src/query_input.rs— the bind path — no longer refuses an unsigned value above the signed range; it carries the exact value.crates/omnigraph/src/engine/typed_value.rsandcrates/omnigraph/src/exec/mutation.rsusetry_fromagainst the declared type, so anI64target still refuses a value outsidei64— with the error naming the value and the range.crates/omnigraph/src/engine/constant.rskeeps a public constant's whole value instead of refusing aU64abovei64::MAX.The saved-plan wire form gains one arm: the pinned serde buffers carry no
i128, so a wide integer travels as its decimal text (crates/omnigraph-planner/src/mirror.rs,WideInteger). A text that is not an integer refuses the decode instead of becoming another value. Theintegerarm keeps its form and its meaning, so existing saved plans still decode; a plan that uses the new arm is one no earlier binary could produce.The frozen v1 engine
This change touches
crates/omnigraph-reference-engine— the frozen v1 engine — in two places only:resolve_to_intand the projection literal now narrow a wide value and refuse it, exactly as v1 behaved before, because a value outside v1'si64range has no v1 meaning. The pinned file hashes incrates/omnigraph-reference-engine/tests/frozen.rsare updated in this pull request, as the change requires, and the v1 behaviour itself is unchanged: narrow, refuse, no new behaviour.Test
crates/omnigraph-compiler/src/query_input.rs— the oldto_param_rejects_unsigned_values_outside_engine_rangeis reversed intoto_param_keeps_unsigned_values_above_the_signed_range_issue_918(u64::MAXbinds exactly), and a new test binds the decimal string"18446744073709551615"to a declaredU64parameter.crates/omnigraph-planner/tests/integration/bound_plan.rs—a_wide_bound_value_reads_back_exactly_issue_918: a saved plan whose value table holdsu64::MAXandi64::MINserializes (the wide value appears as its decimal text) and reads back equal.crates/omnigraph-gqt/cases/issue_918_u64_param_above_i64_max.gqt— the end-to-end case: aU64column seeded with18446744073709551615, a read with the parameter bound to that value (in the JSON-number form and in the decimal-string form the wire uses) returns exactly the seeded row, a mutation with the same parameter writes a row carrying the full value, and the final read shows both rows with the value intact — exact, not rounded.crates/omnigraph-gqt/cases/range_constraint_compares_integers_exactly.gqt— its note said "mutation parameters stop at i64::MAX", which is no longer true; the note is corrected and a step shows aU64mutation parameter abovei64::MAXreaching the@rangecheck.docs/user/queries/index.md— the parameter's range joins the declarations paragraph and the decimal-string advice joins the JSON input paragraph. The page was already exactly at the 350-line cap thatscripts/check-docs.pyenforces, so each fact had to fit an existing paragraph; two pointer sentences that stood alone now close the paragraph they belong with.Judgement calls, flagged
BOUND_PLAN_VERSIONis bumped to 11. Theintegerarm keeps its wire form and meaning, so a plan that holds only in-range values still decodes unchanged. Thewide_integerarm is new, though, and version 10 has to name one schema: an older reader that meets the arm refuses on the version before it reaches the arm, and says so. Regenerate saved plans; the change ships abreakingnote.i64, so18446744073709551615written directly in a.gqfile is still a parse error. That is a language decision and not part of this issue.i64::MAXwas added; results still come from Arrow columns, which already carriedUInt64exactly.Note on validation
I did not build or run anything locally — CI on this pull request is the judge. The change adds no dependency and no new process or filesystem lifecycle.
Model Used
MiniMax M3.1 Flash Free via the opencode agent harness (
minimax-m3.1-flash-free)