fix(sql): raise 23502 for NULL primary keys instead of committing them - #302
Open
EnRaiha wants to merge 1 commit into
Open
fix(sql): raise 23502 for NULL primary keys instead of committing them#302EnRaiha wants to merge 1 commit into
EnRaiha wants to merge 1 commit into
Conversation
A NULL primary key — explicit, or by omitting the column — used to commit on the document (schemaless) and kv engines. Uniqueness still applied to real values, so the key was neither unique nor non-null for those rows, and several NULL-keyed rows coexisted per collection. The damage was silently inconsistent: SELECT and DELETE found all NULL-keyed rows while count(*) found one, because one row stored an explicit null and the other had the field absent, and the two paths treated absence differently. PRIMARY KEY now implies NOT NULL at plan time on every engine with a declared key. INSERT, UPSERT, and INSERT ... ON CONFLICT rows are checked after type coercion and before engine dispatch: a row that omits the declared primary-key column or binds it to NULL raises SQLSTATE 23502 (not_null_violation) instead of falling through to the "fresh surrogate" identity path. Collections whose key is synthetic are exempt and keep minting fresh identities: _rowid collections and schemaless collections created without a column list (their only column is the auto PK). The error is typed end to end: a new SqlError::NotNullViolation maps to a new crate::Error::NotNullViolation, to the public NodeDbError code 1207 (msgpack tag 80), and to SQLSTATE 23502 on pgwire and the native protocol — mirroring the undefined_function path. document_strict used to reject the same input only later, inside tuple serialization, as an internal error; the plan-time gate now surfaces the clean 23502 there too. Uniqueness for real values is unchanged (23505). Existing NULL-keyed rows committed before this fix remain in place; identifying and repairing them is tracked separately. Fixes NodeDB-Lab#293
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(sql): raise 23502 for NULL primary keys instead of committing them
Fixes #293
Problem
A NULL primary key — explicit, or by omitting the column — committed on the document (schemaless) and kv engines. Uniqueness still applied to real values, so the key was neither unique nor non-null for those rows, and several NULL-keyed rows coexisted per collection. Readback was silently inconsistent:
SELECTandDELETEfound all NULL-keyed rows whilecount(*)found one — one row stored an explicit null, the other had the field absent, and the two paths treated absence differently.Fix
PRIMARY KEY implies NOT NULL at plan time on every engine with a declared key.
INSERT,UPSERT, andINSERT ... ON CONFLICTrows are checked after type coercion, before engine dispatch:INSERT ... (id, v) VALUES (NULL, ...)declared doc23502INSERT ... (v) VALUES (...)(pk omitted), declared doc2350223502document_strictserialization error (binary_tuple)23502UPSERTwith NULL key (doc/kv)2350223505uniquenessA row that omits the declared key or binds it to NULL used to fall through to the "fresh surrogate" identity path — silently minting a new identity for what the caller declared as the row's key. Collections whose key is synthetic legitimately mint fresh identities and are exempt:
_rowidcollections and schemaless collections created without a column list (their only catalog column is the auto PK, matching the #292 closed-schema line).The error is typed end to end:
SqlError::NotNullViolation→crate::Error::NotNullViolation→ publicNodeDbErrorcode 1207 / msgpack tag 80 → SQLSTATE 23502 on pgwire and native, mirroring theundefined_functionpath.Out of scope
INSERT ... SELECTsources flow through a separate conversion path; a NULL key there still takes the old surrogate route (tracked separately).keycolumn): a NULL/omitted key is rejected by the same gate — the key bytes are the identity there too.PRIMARY KEY DEFAULT NULL(pathological DDL) can still reach the surrogate route when the default evaluates to nothing; noted, not chased.How to test
Verified
not_null_pk_23502: 4 passed.cargo fmt --all -- --checkclean; clippy-D warningsclean on nodedb-sql, nodedb-types, nodedb; unit 864 + 688 passed.