Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/workerd/util/sqlite-test.c++
Original file line number Diff line number Diff line change
Expand Up @@ -1799,6 +1799,26 @@ KJ_TEST("SQLite memory metering enforces SQLITE_NOMEM when limit is exceeded") {
KJ_EXPECT_THROW_MESSAGE("out of memory: SQLITE_NOMEM", db.run("SELECT hex(zeroblob(65536))"));
}

KJ_TEST("SQLite PRAGMA quick_check succeeds on a schema with many tables") {
auto dir = kj::newInMemoryDirectory(kj::nullClock());
SqliteDatabase::Vfs vfs(*dir);
SqliteDatabase db(vfs, kj::Path({"foo"}), kj::WriteMode::CREATE | kj::WriteMode::MODIFY);

// PRAGMA quick_check compiles checks for every table and index into a single statement, so its
// VDBE program grows with the schema. SQLITE_LIMIT_VDBE_OP must leave room for realistic schemas
// like this one; exceeding it fails the prepare with a misleading SQLITE_NOMEM.
for (auto i: kj::zeroTo(100)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING] This never exercises the previous 25,000-op ceiling: running the same 100-table schema with .limit vdbe_op 25000 returned ok in SQLite 3.45.1, so reverting the production limit would still pass this test. Grow the fixture to a size demonstrated to fail at the old cap (500 table/index pairs fails there and passes at 250,000 in that version).

Suggested change
for (auto i: kj::zeroTo(100)) {
for (auto i: kj::zeroTo(500)) {

db.run({.regulator = SqliteDatabase::TRUSTED},
kj::str("CREATE TABLE t", i,
" (id INTEGER PRIMARY KEY, a TEXT NOT NULL, b INTEGER, c BLOB);", "CREATE INDEX t", i,
"_a ON t", i, "(a);"));
}

auto query = db.run("PRAGMA quick_check");
KJ_ASSERT(!query.isDone());
KJ_EXPECT(query.getText(0) == "ok");
}

KJ_TEST("SQLite memory metering tracks allocations correctly") {
auto dir = kj::newInMemoryDirectory(kj::nullClock());
SqliteDatabase::Vfs vfs(*dir);
Expand Down
2 changes: 1 addition & 1 deletion src/workerd/util/sqlite.c++
Original file line number Diff line number Diff line change
Expand Up @@ -1447,7 +1447,7 @@ void SqliteDatabase::setupSecurity(sqlite3* db) {
// Enforces limits on UNION/UNION ALL/INTERSECT/etc
// https://www.sqlite.org/limits.html#max_compound_select
sqlite3_limit(db, SQLITE_LIMIT_COMPOUND_SELECT, 5);
sqlite3_limit(db, SQLITE_LIMIT_VDBE_OP, 25000);
sqlite3_limit(db, SQLITE_LIMIT_VDBE_OP, 250000);
// For SQLITE_LIMIT_FUNCTION_ARG we use the default instead of the "security" recommendation
// because there are too many valid use cases for large argument lists, especially json_object.
sqlite3_limit(db, SQLITE_LIMIT_FUNCTION_ARG, 127);
Expand Down
Loading