Skip to content

ANALYZE creates spurious NULL-idx rows in sqlite_stat1 for tables with indexes #6299

@Golliath82pr

Description

@Golliath82pr

Summary

When ANALYZE runs on a table that has indexes, Turso creates a spurious (table_name, NULL, rowcount) entry in sqlite_stat1 alongside the correct per-index entries. SQLite only creates the NULL-idx entry for tables that have no indexes (as a fallback row count estimate).

Reproduction

CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT);
INSERT INTO t1 VALUES(1, 'x');
CREATE INDEX i1 ON t1(a);
ANALYZE t1;

SELECT tbl,
       CASE WHEN idx IS NULL THEN '<NULL>' ELSE idx END,
       stat
FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;

Turso (wrong — extra row):

t1|<NULL>|1
t1|i1|1 1

SQLite (correct):

t1|i1|1 1

Table without indexes — both correct (control)

CREATE TABLE t2(a TEXT, b INTEGER);
INSERT INTO t2 VALUES('x', 1);
ANALYZE t2;

Both produce t2|<NULL>|1 — correct. The NULL-idx entry is appropriate when there are no indexes.

Multi-column index — same bug

CREATE TABLE t3(id INTEGER PRIMARY KEY, a TEXT, b INTEGER);
INSERT INTO t3 VALUES(1, 'x', 10);
INSERT INTO t3 VALUES(2, 'x', 20);
CREATE INDEX i3 ON t3(a, b);
ANALYZE t3;

Turso: t3|<NULL>|2 + t3|i3|2 2 1 (extra row)
SQLite: t3|i3|2 2 1 only

200-row table with two indexes

Turso produces 3 rows (NULL + idx_cat + idx_val). SQLite produces 2 (idx_cat + idx_val). Per-index statistics match — only the extra NULL-idx row differs.

Controls

Scenario SQLite Turso Bug?
Table with index(es) idx entries only idx entries + NULL entry YES
Table without indexes NULL entry NULL entry No
Per-index stat values correct correct No
Query results after ANALYZE correct correct No

Root Cause

The stat1 insertion code in core/translate/analyze.rs unconditionally creates a (table_name, NULL, rowcount) entry for every analyzed table, then creates per-index entries. SQLite only creates the NULL-idx entry when the table has no indexes.

Impact

  • Metadata incompatibility: Applications reading sqlite_stat1 directly (ORMs, schema tools) see unexpected rows. Files written by Turso contain entries SQLite wouldn't generate.
  • Potential optimizer divergence: The extra NULL-idx entry provides a table scan cost estimate that wouldn't normally exist, potentially causing different (but still correct) query plan choices vs SQLite.

Version

v0.5.3 (commit 09c149a)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions