Ensure primary keys created via the JSON API are NOT NULL#2826
Ensure primary keys created via the JSON API are NOT NULL#2826PranavMishra28 wants to merge 1 commit into
Conversation
The table-create JSON API built its own CREATE TABLE without marking primary key columns NOT NULL, so a text or composite primary key could be created nullable. SQLite then allows rows with a NULL primary key, which cannot be viewed, edited or deleted. Force primary key columns NOT NULL, leaving a lone integer primary key alone since it aliases the rowid and is never NULL. Refs simonw#2807 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@simonw lemme know when you can throw some eyes here or require any other changes |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3956e63650
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| not_null = [ | ||
| column.name | ||
| for column in columns | ||
| if column.not_null or column.name in pk_names |
There was a problem hiding this comment.
Handle mixed-case primary key names
When the request names a primary key with different casing from the column, for example column Code with pk: "code", SQLite still resolves that primary key to the column, but this membership check is case-sensitive and leaves the real column out of not_null. That means this API can still create a nullable text/blob/float primary key for otherwise valid input, leaving the same unviewable/undeletable NULL-PK rows this change is meant to prevent.
Useful? React with 👍 / 👎.
What was broken
The table-create JSON API (
/db/-/create) builds its ownCREATE TABLE(the explicit-columns path inTableCreateView.create_table) without marking primary key columnsNOT NULL. So a text or composite primary key is created nullable:→
CREATE TABLE "t" ("code" TEXT PRIMARY KEY, ...)—codecan beNULL.Why it matters
SQLite permits a
NULLvalue in any primary key that isn't a single-columnINTEGER PRIMARY KEY(and lets multiple rows share theNULLkey). As #2805 shows, such rows can't be viewed, edited or deleted through Datasette — the row URL resolves toNone/"". #2807 asks to "stem the bleeding" by disallowing null primary keys for tables created via the JSON API.What the fix does
In the explicit-columns create path — the one Simon described as "any time we run our own CREATE TABLE we set not null on any primary key we create" — primary key columns are added to the
not_nullset passed totable.create(). A single-columnINTEGER PRIMARY KEYis skipped because it aliases the rowid and can never beNULL, so no redundant constraint is emitted (and the commonidschema is unchanged).Result:
"code" TEXT PRIMARY KEY NOT NULL; composite keys getNOT NULLon every key column.What it deliberately does not change
INTEGER PRIMARY KEYoutput is untouched (stillINTEGER PRIMARY KEY), so existing schemas/tests/docs are unaffected.rows/insert_allinference path is not changed here. That path can also create a nullable text pk, but because its column types are inferred rather than declared, the single-integer-pk (rowid) exception can't be applied reliably in the same way. Happy to follow up on that path if you'd like it covered too — henceRefs #2807rather thanFixes.Testing
test_create_table_primary_keys_are_not_nullcovers a single text pk and a composite pk (schema +pragma_table_infonotnull), and asserts non-pk columns stay nullable.tests/test_api_write.py(148 passed), plustests/test_schema_endpoints.pyandtests/test_api.py, all green;black --checkandruff checkclean.Refs #2807
Developed with Claude Code; reviewed and tested by Pranav before marking ready for review.