All rules Rule SL029 · TY
SQLite · beta
critical
TY · Type choices
free in the CLI, --engine=sqlite

Why can't a WITHOUT ROWID table use AUTOINCREMENT?

AUTOINCREMENT on a WITHOUT ROWID table

Critical: SQLite refuses the statement, so the migration fails here.

What happens

AUTOINCREMENT only exists for the rowid, and a WITHOUT ROWID table has none: SQLite refuses the CREATE TABLE with "AUTOINCREMENT not allowed on WITHOUT ROWID tables". The table is not created and the migration stops.

Why it is dangerous on a populated table

SQLite holds one write lock for the whole database, so anything that rebuilds a table blocks every writer for as long as the copy takes, and that time grows with the table.

Fires on

CREATE TABLE sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, token TEXT) WITHOUT ROWID;

The safe pattern

Drop the keyword: or drop WITHOUT ROWID if a monotonically increasing integer key is the point of the table.

CREATE TABLE sessions (id INTEGER PRIMARY KEY, token TEXT);

Fixtures

The rule ships with these files and the test suite runs them on every change: the first set must fire, the second must stay silent.

Fires (1)

autoincrement without rowid
CREATE TABLE sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, token TEXT) WITHOUT ROWID;

Stays silent (2)

rowid autoincrement
CREATE TABLE sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, token TEXT) STRICT;
without rowid
CREATE TABLE sessions (id INTEGER PRIMARY KEY, token TEXT) WITHOUT ROWID;

How to check locally

Catch this before it ships

SQLite support is in beta: this rule runs locally in the free CLI, static only, and not in the hosted service yet. No install, nothing leaves your machine:

npx bolvrk check migration.sql --engine=sqlite