.extract() and .lookup() no longer extract null values, closes #186

- table.extract() and the sqlite-utils extract command now skip rows
  where every extracted column is null: the new foreign key column is
  left null and no all-null record is added to the lookup table. Rows
  with at least one non-null extracted column are extracted as before.
- The extracts= option to insert() and friends keeps None values as
  null instead of creating a lookup record for them - previously each
  insert batch added a duplicate null row to the lookup table.
- table.lookup() compares values using IS rather than = so lookup
  values containing None match existing rows correctly, instead of
  inserting a duplicate row on every call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Simon Willison 2026-07-06 20:39:29 -07:00
commit 2616dec795
8 changed files with 176 additions and 7 deletions

View file

@ -67,3 +67,51 @@ def test_extracts(fresh_db, kwargs, expected_table, use_table_factory):
{"id": 2, "species_id": 1},
{"id": 3, "species_id": 2},
] == list(fresh_db["Trees"].rows)
def test_extracts_null_values(fresh_db):
# https://github.com/simonw/sqlite-utils/issues/186
# Null values should stay null, not be extracted into the lookup table
fresh_db["Trees"].insert_all(
[
{"id": 1, "species_id": "Oak"},
{"id": 2, "species_id": None},
{"id": 3, "species_id": "Palm"},
{"id": 4, "species_id": None},
],
extracts={"species_id": "Species"},
)
assert list(fresh_db["Species"].rows) == [
{"id": 1, "value": "Oak"},
{"id": 2, "value": "Palm"},
]
assert list(fresh_db["Trees"].rows) == [
{"id": 1, "species_id": 1},
{"id": 2, "species_id": None},
{"id": 3, "species_id": 2},
{"id": 4, "species_id": None},
]
def test_extracts_null_values_list_mode(fresh_db):
# Same as test_extracts_null_values but for list-based records
fresh_db["Trees"].insert_all(
[
["id", "species_id"],
[1, "Oak"],
[2, None],
[3, "Palm"],
[4, None],
],
extracts={"species_id": "Species"},
)
assert list(fresh_db["Species"].rows) == [
{"id": 1, "value": "Oak"},
{"id": 2, "value": "Palm"},
]
assert list(fresh_db["Trees"].rows) == [
{"id": 1, "species_id": 1},
{"id": 2, "species_id": None},
{"id": 3, "species_id": 2},
{"id": 4, "species_id": None},
]