sqlite-utils/tests
feiiiiii5 8e5dfdd409
analyze_column(): handle null values in the common-value analysis
Three ways a column containing nulls produced a wrong answer, all from
nulls not being accounted for.

1. The "single distinct value" shortcut. count(distinct col) skips
   nulls in SQL, so a column holding nulls plus one other value reports
   num_distinct == 1 and took the shortcut, which reports that one value
   with a count of total_rows:

       insert [{'c': None}, {'c': None}, {'c': 'a'}, {'c': 'a'}]
       analyze_column('c').most_common   # was [(None, 4)]
       select c, count(*) group by c     # is [(None, 2), ('a', 2)]

   The shortcut now requires that the column has no nulls, so those
   columns fall through to the group by like any other.

2. The tie-break sort compared the values directly, so a null and a
   string with the same count raised TypeError:

       analyze_column('c', common_limit=5)
       # TypeError: '<' not supported between instances of 'NoneType' and 'str'

   sort_key() keeps nulls out of the comparison and sorts them last,
   which keeps the existing ordering of non-null ties - the test for
   'Terryterryterry' ahead of 'Kumar' at equal counts still passes.

3. truncate() turned a null into the string 'None', so a null was
   indistinguishable from a column really holding 'None'. That is the
   path analyze-tables takes, since it passes value_truncate=80.

num_distinct itself is unchanged: it still follows SQL and reports 0 for
an all-null column, which test_analyze_table_column_all_nulls pins. But
num_distinct can therefore be lower than len(most_common) when a column
holds nulls alongside all-distinct values - {'c': None, 'a', 'b'} gives
num_distinct 2 and three most_common entries. Whether null should count
as a distinct value is a question about what the number means, so I have
left it alone rather than change an output the tests already pin.

Tests: 6 new cases in tests/test_analyze.py, all failing on 6bc1d33.
Suite 1506 passed, 16 skipped. mypy, pyright, flake8, ty, black and
cog --check clean.
2026-09-28 18:55:53 +08:00
..
sniff sqlite-utils insert --sniff option, closes #230 2021-02-14 11:23:12 -08:00
__init__.py Initial project layout + database table creation tools 2018-07-28 06:46:17 -07:00
conftest.py Fixes for Ruff>=0.16.0 (#814) 2026-07-25 14:53:12 -07:00
ext.c Support entrypoints for --load-extension (#473) 2022-08-26 22:55:47 -07:00
test_analyze.py analyze_column(): handle null values in the common-value analysis 2026-09-28 18:55:53 +08:00
test_analyze_tables.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_atomic.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_attach.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_cli.py Support ANY column types for strict tables 2026-08-12 16:44:01 -07:00
test_cli_bulk.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_cli_convert.py Use quoted SQL identifiers in convert --dry-run, closes #829 2026-08-12 14:14:27 -07:00
test_cli_insert.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_cli_memory.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_cli_migrate.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_column_affinity.py Support ANY column types for strict tables 2026-08-12 16:44:01 -07:00
test_column_casing.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_constructor.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_conversions.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_convert.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_create.py Support ANY column types for strict tables 2026-08-12 16:44:01 -07:00
test_create_table_parser.py Preserve composite UNIQUE constraints in transforms 2026-08-12 18:46:18 -07:00
test_create_view.py Fixes for Ruff>=0.16.0 (#814) 2026-07-25 14:53:12 -07:00
test_default_value.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_delete.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_docs.py Fixes for Ruff>=0.16.0 (#814) 2026-07-25 14:53:12 -07:00
test_duplicate.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_enable_counts.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_extract.py Support ANY column types for strict tables 2026-08-12 16:44:01 -07:00
test_extracts.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_foreign_keys.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_fts.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_get.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_gis.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_hypothesis.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_insert_files.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_introspect.py Fixed introspection of default values TRUE / FALSE / NULL 2026-08-12 14:19:38 -07:00
test_list_mode.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_lookup.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_m2m.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_migrations.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_mutator_transactions.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_plugins.py Fixes for Ruff>=0.16.0 (#814) 2026-07-25 14:53:12 -07:00
test_query.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_recipes.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_recreate.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_register_function.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_rows.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_rows_from_file.py Fix resource leaks caused by buffered readers 2026-09-22 11:35:41 -07:00
test_sniff.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_suggest_column_types.py Fixes for Ruff>=0.16.0 (#814) 2026-07-25 14:53:12 -07:00
test_tracer.py Fixes for Ruff>=0.16.0 (#814) 2026-07-25 14:53:12 -07:00
test_transform.py transform: coerce empty strings to NULL when converting TEXT columns to numeric types (#805) 2026-08-13 12:56:51 -07:00
test_update.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00
test_upsert.py Fixes for Pyright, closes #833 2026-08-12 14:03:04 -07:00
test_utils.py Fixes for Ruff>=0.16.0 (#814) 2026-07-25 14:53:12 -07:00
test_wal.py Use db.table() and db.view() in tests, closes #838 2026-08-12 14:03:04 -07:00