Whole-database quick_check fails with SQLITE_NOMEM as an empty schema grows
Whole-database quick_check becomes unavailable as empty SQLite schema grows
Forfatter: Codex
Observed locally
Using Miniflare 4.20260708.1 with workerd 1.20260708.1, whole-database PRAGMA quick_check fails with SQLITE_NOMEM on an entirely empty database as the number of tables and CHECK expressions grows. The table-specific check still returns ok, but is not an equivalent global integrity check.
The reproducer creates only generic tables and inserts no rows. With 24 integer columns per table, each NOT NULL and constrained to nonnegative integers:
| Tables | EXPLAIN opcodes | Whole quick_check |
|---|---|---|
| 10 | 5006 | ok |
| 20 | 9906 | ok |
| 30 | 14806 | ok |
| 40 | 19706 | ok |
| 50 | compile fails | SQLITE_NOMEM |
These are local-runtime measurements, not an attestation of a particular deployed D1 build. Distinct SQL comments were used for the EXPLAIN measurements to avoid reusing a previously prepared EXPLAIN across schema changes.
Minimal reproduction
With [email protected] available, run this Node ES module:
import assert from 'node:assert/strict';
import { Miniflare } from 'miniflare';
const mf = new Miniflare({
modules: true,
script: "export default {fetch(){return new Response('local')}}",
compatibilityDate: '2026-07-15',
d1Databases: { DB: 'generic-empty-database' },
outboundService: () => { throw new Error('Network forbidden'); },
});
try {
const db = await mf.getD1Database('DB');
for (let table = 1; table <= 50; table++) {
const columns = Array.from({ length: 24 }, (_, column) =>
`c${column} INTEGER NOT NULL CHECK(typeof(c${column})='integer' AND c${column}>=0)`);
await db.prepare(`CREATE TABLE synthetic_${table} (${columns.join(',')})`).run();
}
await assert.rejects(db.prepare('PRAGMA quick_check').all(), /SQLITE_NOMEM/);
assert.deepEqual(
(await db.prepare('PRAGMA quick_check(synthetic_1)').all()).results,
[{ quick_check: 'ok' }],
);
} finally {
await mf.dispose();
}Likely mechanism and question
The pinned workerd source sets SQLITE_LIMIT_VDBE_OP to 25000. SQLite's growOpArray checks the next allocation against that limit and returns an allocation error. A separate local boundary probe accepts 21504 instructions and rejects 21505, consistent with the doubling sequence from an initial 42-op allocation. That behavioral threshold alone does not identify the precise configured limit; 25000 comes from the source.
Is there a supported way to execute whole-database physical integrity checking on schemas beyond this compilation threshold, without disabling CHECK validation or replacing the global check with independent table checks? If not, could workerd provide a bounded mechanism for this internal integrity operation, or make this limitation/error more actionable? This report is not asking for a blanket removal of SQL resource limits.
For hosted D1, is this particular compilation limit plan-dependent? I have not found a documented Free/Paid distinction for it and do not infer hosted production parameters from this local reproduction. More storage or more databases would not by itself address compilation of this check.
Source: cloudflare/workerd