Conversation
|
ACTION NEEDED The PR title and description are used as the merge commit message. Please update your PR title and description to match the specification. For details on the error please inspect the "PR Title Check" action. |
|
Important Format specification voteThis PR modifies the Lance format specification, so it requires 3 binding +1 votes from PMC members (excluding the proposer), at least one of them on the latest commit, and a minimum 72-hour voting period, weekends excluded, before it can merge. Vote by approving this PR (+1) or requesting changes (−1, a veto). See the voting process. Approvals carry over across pushes, so a rebase or a typo fix does not send everyone back to re-vote. Whoever approves the latest commit is vouching that nothing substantive has changed since the earlier approvals; if something has, ask for fresh votes. Status: ❌ Blocked — 1 of 3 required approvals
Updated automatically by the format-spec vote gate, which re-checks every 15 minutes — just voted? Re-check now (press Run workflow; leave the input blank to re-check every open format PR). A PMC member may apply the |
2990c06 to
b81171e
Compare
|
Both points taken; pushed as On On the completeness claim — agreed, and scoped. The field comment now says recording the type "completes the description of the extracted values only. These details still do not record the target index's format version, so they are not a general, lossless description of the index and must not be read as one." On the target index version — I re-evaluated folding #4628 into this PR, and it stays out, but your comment surfaced something worse than a documentation gap, so recording it here. The version problem is not only that That already misreports today: Adding Since it changes what is written to the manifest, it needs its own compatibility analysis and its own PR rather than riding along on a format vote. Full write-up, including the retraction of the earlier (wrong) reasoning for leaving #4628 out, is in the decision record. |
`JsonIndexDetails` carries only a path and the target index's type identity, so the persisted details are not sufficient to rebuild an equivalent JSON index. A JSON value has no fixed type, and when the type is not recorded the wrapper infers it by reading the type tag of the first non-null value at the path, falling back to Utf8 when every value is null. That makes a rebuild driven purely by details non-deterministic with respect to the rows it happens to see: a rebuild over a different row set, or a build split across workers each training over disjoint fragments, can infer different types with nothing to reconcile them. Add `JsonIndexDetails.target_data_type`, a `JsonTargetDataType` enum whose variants are the image of the JSONB type tags, so it covers every JSON value type. `JSON_TARGET_DATA_TYPE_UNSPECIFIED` means the type was not recorded and must be inferred as before, so existing indices load, query and compact unchanged. The field carries a normative requirement: an implementation that rewrites these details without retraining the target index must copy the value through unchanged, including a value it does not recognize, so that a variant added by a later revision is not erased by an older reader that touches the index. This is the specification only. The three `JsonIndexDetails` literals in `json.rs` name the new field so the build stays green; they record UNSPECIFIED, which is the value the format already assigns to details written before the field existed, so runtime behavior is unchanged. The writers and readers that populate it follow in the next PR. Part of #9256 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two review points from the format gate on the JsonIndexDetails proposal. UNSPECIFIED said the type must be inferred by decoding the data again. That is wrong as a normative rule: some target indices retain the Arrow type they were trained on, and an implementation that followed the old sentence would re-infer from a different row set and could pick a different type -- the exact inconsistency this field exists to prevent. The recovery order is now explicit: ask the target first, and infer only when it cannot answer. The field comment also claimed that recording the type makes these details a complete description of the index. It does not: the target index's format version is still absent. Scope the claim to the extracted values. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous revision left two rules that contradict each other for a legacy index whose target can report its trained type. UNSPECIFIED said an implementation recovering the type authoritatively should record it, while field 3 said a rewrite must copy the value through unchanged. For a recorded 0 whose target reports INT64 the first says write 2 and the second says keep 0, and no implementation can satisfy both. Make the exception explicit in both places. The preservation rule now covers every value that records a type -- copied through unchanged, including one the implementation does not recognize -- and names UNSPECIFIED as the sole exception, because it records nothing to preserve. UNSPECIFIED, in turn, says a rewrite may replace it with an authoritatively recovered type, and must not replace it with an inferred one, which would persist a row-dependent guess as recorded fact. This is the behavior the stacked implementation already has; the spec now describes it without ambiguity. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
b81171e to
55cb274
Compare
|
Good catch — that was a real contradiction, not just loose wording. For a legacy Field 3's rule is now scoped to values that actually record a type:
And
That last sentence matters for the case you raised earlier: a compaction sees whatever fragments it was given, so letting a rewrite persist an inferred type would bake the disjoint-subset inconsistency into the details permanently — worse than leaving No implementation change needed — this is what #9304 already does, and both halves are pinned by tests: |
There was a problem hiding this comment.
✅ Gate recommendation: approve.
The author’s follow-up resolves the remaining contract contradiction: UNSPECIFIED is now the sole upgrade exception, only an authoritatively recovered type may replace it, and every recorded nonzero value—including future enum variants—must remain unchanged. The additive protobuf shape remains readable across versions, and the stacked implementation exercises both the legacy-upgrade and raw-value preservation paths.
A JSON value has no fixed type, so a JSON index picks one type for the path it indexes and trains its target index on that.
JsonIndexDetailsdoes not record which type it picked, so the details are not enough to rebuild an equivalent index. A rebuild re-infers the type from the first non-null value it sees, falling back to string when everything is null — so a rebuild over different rows, or a build split across workers each training over different fragments, can end up with different types and nothing to reconcile them.This adds
JsonIndexDetails.target_data_type, an enum covering every JSON value type.JSON_TARGET_DATA_TYPE_UNSPECIFIEDmeans the type was not recorded, and those indices keep loading, querying and compacting exactly as before.The field also carries a requirement on readers: an implementation that rewrites these details without retraining the target index must copy the value through unchanged, including one it does not recognize. Otherwise a variant added by a later revision is erased by any older reader that touches the index.
Not included
This is the specification only, as
protos/AGENTS.mdand the gatekeeper's note on #9261 ask. The threeJsonIndexDetailsliterals injson.rsname the new field so the build stays green; they recordUNSPECIFIED, which is the value the format already assigns to details written before the field existed, so nothing changes at runtime. The writers and readers that populate it are in #9304, stacked on this PR.No API for building an index from details — lance has no such entry point for scalar indices, and adding one is a larger design question. #4628 was considered and left out; that reasoning, and the encodings rejected for this field, are in the decision record.
Part of #9256
🤖 Generated with Claude Code
Stack created with GitHub Stacks CLI • Give Feedback 💬