mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-19 06:54:25 +02:00
`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>
|
||
|---|---|---|
| .. | ||
| sniff | ||
| __init__.py | ||
| conftest.py | ||
| ext.c | ||
| test_analyze.py | ||
| test_analyze_tables.py | ||
| test_atomic.py | ||
| test_attach.py | ||
| test_cli.py | ||
| test_cli_bulk.py | ||
| test_cli_convert.py | ||
| test_cli_insert.py | ||
| test_cli_memory.py | ||
| test_cli_migrate.py | ||
| test_column_affinity.py | ||
| test_constructor.py | ||
| test_conversions.py | ||
| test_convert.py | ||
| test_create.py | ||
| test_create_view.py | ||
| test_default_value.py | ||
| test_delete.py | ||
| test_docs.py | ||
| test_duplicate.py | ||
| test_enable_counts.py | ||
| test_extract.py | ||
| test_extracts.py | ||
| test_fts.py | ||
| test_get.py | ||
| test_gis.py | ||
| test_hypothesis.py | ||
| test_insert_files.py | ||
| test_introspect.py | ||
| test_list_mode.py | ||
| test_lookup.py | ||
| test_m2m.py | ||
| test_migrations.py | ||
| test_plugins.py | ||
| test_query.py | ||
| test_recipes.py | ||
| test_recreate.py | ||
| test_register_function.py | ||
| test_rows.py | ||
| test_rows_from_file.py | ||
| test_sniff.py | ||
| test_suggest_column_types.py | ||
| test_tracer.py | ||
| test_transform.py | ||
| test_update.py | ||
| test_upsert.py | ||
| test_utils.py | ||
| test_wal.py | ||