Skip to content

Don't re-run writes while inferring strict column types - #370

Open
quinnj wants to merge 1 commit into
masterfrom
claude/strict-no-reexecute
Open

quinnj wants to merge 1 commit into
masterfrom
claude/strict-no-reexecute

Conversation

@quinnj

@quinnj quinnj commented Oct 22, 2026

Copy link
Copy Markdown
Member

With strict=true, a column whose first value is NULL is typed by stepping ahead to a non-NULL row and then rewinding with sqlite3_reset + sqlite3_step. Rewinding re-runs the statement, so writes that return rows ran twice:

db = SQLite.DB()
DBInterface.execute(db, "CREATE TABLE t (id INTEGER PRIMARY KEY, a INT, b TEXT)")
stmt = DBInterface.prepare(db, "INSERT INTO t(a) VALUES (1),(2) RETURNING id, b")
DBInterface.execute(stmt, (); strict=true) |> columntable  # (id = [3, 4], b = [missing, missing])
DBInterface.execute(db, "SELECT count(*) AS n FROM t") |> columntable  # (n = [4],) -- 4 rows, not 2

UPDATE ... RETURNING likewise applied its update twice. #354 stopped the scan for statements that return no row; this covers writes that do return rows.

  • Statements that write (sqlite3_stmt_readonly == 0) are no longer scanned. A column whose first value is NULL gets its declared type, or Any when it has none.
  • Strict Any columns (for example a column declared BLOB) read every value as blob bytes, so an integer 1 came back as UInt8[0x31] and 'txt' as its bytes. Values in an Any column are now read by their own storage class, as non-strict queries already do.

Verification: two new testsets in "strict mode" (6 assertions fail on master, all pass here); full Pkg.test() passes locally on Julia 1.12 (SQLite 505/505 plus the lifetime, aggregate, BLOB and multi-statement sets).

🤖 Generated with Claude Code

With `strict=true`, a column whose first value is NULL was typed by stepping
ahead to a non-NULL row, then rewinding with sqlite3_reset + sqlite3_step.
Rewinding re-runs the statement, so `INSERT ... RETURNING` inserted its rows
twice and `UPDATE ... RETURNING` applied its update twice. Statements that
write (sqlite3_stmt_readonly == 0) are no longer scanned; such a column gets
its declared type, or `Any` when it has none.

Strict `Any` columns (e.g. declared BLOB) also read every value as blob
bytes, so an integer 1 came back as UInt8[0x31]. Values in an `Any` column
are now read by their own storage class, as in non-strict queries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.77%. Comparing base (d3f97db) to head (82c7ac7).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #370      +/-   ##
==========================================
+ Coverage   96.75%   96.77%   +0.02%     
==========================================
  Files           5        5              
  Lines         770      775       +5     
==========================================
+ Hits          745      750       +5     
  Misses         25       25              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant