sqlite-utils/sqlite_utils
Johnson K C dad463e9c2 Don't extract NULL values into a lookup row (#186)
`table.extract()` built its lookup table with
`INSERT OR IGNORE ... SELECT DISTINCT <cols> FROM <table>`, which included
the all-NULL combination. That created a spurious lookup row for NULL and
pointed every NULL source row at it, instead of leaving those rows with a
NULL foreign key.

Before:
    db["creatures"].extract("type")
    # type lookup: [{"id": 1, "type": None}, {"id": 2, "type": "dog"}]
    # creatures:   Simon -> type_id=1, Natalie -> type_id=1, Cleo -> type_id=2

After:
    # type lookup: [{"id": 1, "type": "dog"}]
    # creatures:   Simon -> type_id=None, Natalie -> type_id=None, Cleo -> type_id=1

A row whose extracted columns are entirely NULL represents "no value", so it
now keeps a NULL foreign key and no lookup row is created for it. The fix adds
a `WHERE NOT (<col> IS NULL AND ...)` guard to the lookup INSERT; the existing
`IS`-based foreign-key UPDATE then leaves those rows NULL automatically (the
subquery finds no matching lookup row).

For multi-column extracts, only the fully-NULL combination is skipped — a
partial-NULL combination (some extracted columns set, others NULL) is a
genuine distinct value and is still extracted and shared between matching rows.

Updates test_extract_works_with_null_values to assert the corrected behaviour
and adds regression tests for the single-column and multi-column cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:10:34 -07:00
..
__init__.py New migrations system, ported from sqlite-migrate (#754) 2026-06-21 09:40:21 -07:00
__main__.py Support 'python -m sqlite_utils', closes #368 2022-01-08 18:33:22 -08:00
cli.py fix: Plugins are still loaded when running tests (#719) 2026-06-21 16:14:45 -07:00
db.py Don't extract NULL values into a lookup row (#186) 2026-06-21 23:10:34 -07:00
hookspecs.py More type annotations (#697) 2025-12-16 22:11:47 -08:00
migrations.py New migrations system, ported from sqlite-migrate (#754) 2026-06-21 09:40:21 -07:00
plugins.py fix: Plugins are still loaded when running tests (#719) 2026-06-21 16:14:45 -07:00
py.typed py.typed file so mypy picks up the types, closes #331 2021-11-14 15:21:04 -08:00
recipes.py More type annotations (#697) 2025-12-16 22:11:47 -08:00
utils.py Test against Python 3.15-dev, bump ty and Black (#738) 2026-05-17 16:52:48 -07:00