75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import { DatabaseSync } from "node:sqlite";
|
|
import { join } from "node:path";
|
|
|
|
/** Engine-dependent cases require the same native limit capability as the worker. */
|
|
export const SQLITE_NATIVE_LIMITS_AVAILABLE = (() => {
|
|
const database = new DatabaseSync(":memory:");
|
|
try {
|
|
return "limits" in database;
|
|
} finally {
|
|
database.close();
|
|
}
|
|
})();
|
|
|
|
/** Exact selected SQLite fixture with committed rows and optional live WAL. */
|
|
export interface SqliteDatabaseFixture {
|
|
readonly path: string;
|
|
readonly walPath: string;
|
|
readonly quotedTable: string;
|
|
close(): void;
|
|
}
|
|
|
|
/** Produce schema and lossless SQLite values through the actual SQLite engine. */
|
|
export const createSqliteDatabaseFixture = (
|
|
root: string,
|
|
wal = false,
|
|
): SqliteDatabaseFixture => {
|
|
const path = join(root, "selected.db");
|
|
const db = new DatabaseSync(path);
|
|
if (wal) db.exec("PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0");
|
|
db.exec(`
|
|
CREATE TABLE records (
|
|
id INTEGER PRIMARY KEY,
|
|
integer_value INTEGER,
|
|
blob_value BLOB,
|
|
real_value REAL,
|
|
nullable_value TEXT,
|
|
text_value TEXT,
|
|
generated_value TEXT GENERATED ALWAYS AS (typeof(integer_value)) VIRTUAL
|
|
);
|
|
CREATE INDEX records_text ON records(text_value);
|
|
CREATE VIEW record_names AS SELECT text_value FROM records;
|
|
CREATE TRIGGER records_insert AFTER INSERT ON records BEGIN SELECT 1; END;
|
|
CREATE TABLE "strange""'; DROP TABLE records;--" ("__proto__" TEXT);
|
|
INSERT INTO "strange""'; DROP TABLE records;--" VALUES ('exact selected table');
|
|
`);
|
|
const insert = db.prepare(
|
|
"INSERT INTO records(id, integer_value, blob_value, real_value, nullable_value, text_value) VALUES (?, ?, ?, ?, ?, ?)",
|
|
);
|
|
insert.run(
|
|
1,
|
|
9223372036854775807n,
|
|
Buffer.from([0, 31, 239, 255]),
|
|
Infinity,
|
|
null,
|
|
"first é\0value",
|
|
);
|
|
insert.run(
|
|
2,
|
|
-9223372036854775808n,
|
|
Buffer.alloc(0),
|
|
-Infinity,
|
|
"present",
|
|
"second",
|
|
);
|
|
db.exec(
|
|
"INSERT INTO records(id, integer_value, blob_value, real_value, nullable_value, text_value) VALUES (3, 0, X'FF', 1.25, NULL, CAST(X'FF00C0AF' AS TEXT))",
|
|
);
|
|
return {
|
|
path,
|
|
walPath: `${path}-wal`,
|
|
quotedTable: "strange\"'; DROP TABLE records;--",
|
|
close: () => db.close(),
|
|
};
|
|
};
|