Repository navigation
Description
Summary
The SQLite table generator can produce a CREATE TABLE whose generated columns depend on each other. Since SQLite 3.44.0 such a statement fails immediately with generated column loop on "...", but that error is not among the expected errors of SQLite3TableGenerator.createTableStatement, so SQLancer throws an AssertionError and the thread stops. This is a false alarm on SQLancer's side, not a SQLite bug.
Why it happens
- Before 3.44.0, SQLite created such a table and reported the loop on first use.
SQLite3Provider.checkTablesForGeneratedColumnLoopshandles exactly that case: it runsSELECT * FROM <table>after the tables are created and expectsgenerated column loopthere. - SQLite check-in af527231c (2023-10-13, released in 3.44.0) made
CREATE TABLEfail at once: "Immediately fail a CREATE TABLE statement that attempts to create a table that has a generated column loop. Legacy allows the table to be created but the table would not be usable for anything."sqlite3EndTablenow runs the sameSELECT*FROMinternally. - Generated columns are generated by default since fix issue #1226 #1238 (fix for The default configuration disable the generation of generated columns in SQLite #1226, 2025-06-05), so the default configuration can reach this.
Reproduction
With the sqlite-jdbc version in pom.xml (3.49.1.0), this statement — produced by the table generator during a run — fails at CREATE TABLE:
CREATE TABLE IF NOT EXISTS t1 (c0 TEXT GENERATED ALWAYS AS (INSTR(((c2) NOT NULL), (((NULL, c1, c1, c1, x''))<>((c1, c2, c2, c1, c2))))) UNIQUE ON CONFLICT REPLACE NOT NULL CHECK ( (0.20277664448948018 IN (c0))) , c1 TEXT UNIQUE ON CONFLICT ABORT GENERATED ALWAYS AS ((((((((((+ (c0)))AND((((c0)) BETWEEN ((c0)) AND ((c0))))))AND(CASE WHEN c0 THEN c2 WHEN c0 THEN c0 WHEN c2 THEN c2 ELSE c0 END)))OR(CAST(c2 AS INTEGER))))OR(((c0)-(c0))))) , c2 TEXT );
-- [SQLITE_ERROR] SQL error or missing database (generated column loop on "c0")c0 reads c1 and c1 reads c0. A minimal form: CREATE TABLE t(a AS (b), b AS (a), c); — accepted by SQLite 3.37.2 (the loop is reported by the following SELECT), rejected at CREATE TABLE by 3.49.1, 3.53.4 and trunk.
In SQLancer the statement is executed through SQLite3TableGenerator.createTableStatement, whose expected errors do not include the message, so it ends in:
java.lang.AssertionError: CREATE TABLE IF NOT EXISTS t1 (c0 TEXT GENERATED ALWAYS AS ...
at sqlancer.common.query.SQLQueryAdapter.checkException(SQLQueryAdapter.java:...)
...
Caused by: org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (generated column loop on "c0")
It is rare per table: running the unmodified generator of current main (d8e6b15) over 4,800,000 tables produced 1 such statement (sqlite-jdbc 3.49.1), not covered by the expected errors. A regular 15-minute run of main (8 threads, --num-queries 20, ~110k databases) did not hit it; we hit it in a long run that creates tables far more often.
Suggested fix
Add the message to the expected errors of SQLite3TableGenerator.createTableStatement:
errors.add("generated column loop");checkTablesForGeneratedColumnLoops can stay for SQLite versions before 3.44. Happy to open a PR.
Activity
[-]# SQLite: AssertionError on CREATE TABLE with a generated column loop (SQLite ≥ 3.44)[/-][+]SQLite: AssertionError on CREATE TABLE with a generated column loop (SQLite ≥ 3.44)[/+]mrigger commentedon Oct 20, 2026
Thanks for reporting this, and sorry for not seeing this issue earlier! It would be great to fix this. If you open a PR, please tag me, so that I won't overlooked it.
signfff commentedon Oct 20, 2026
Thanks, @mrigger — PR is up at #1373: it adds the error to
createTableStatement's expected errors and a regression test that replays the statement which triggered the thread loss.