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
5 changes: 4 additions & 1 deletion src/examples/IDBMirrorVFS.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,10 @@ export class IDBMirrorVFS extends FacadeVFS {
file.blocks.set(0, newBlock);
block = newBlock;
}
block.set(pData, iOffset);
// pData is a Uint8ArrayProxy, which has no indexed access, so set()
// would read undefined at every index and store zeroes. subarray()
// returns a real Uint8Array over the same bytes.
block.set(pData.subarray(), iOffset);
file.blockSize = Math.max(file.blockSize, iOffset + pData.byteLength);
}
return VFS.SQLITE_OK;
Expand Down
2 changes: 2 additions & 0 deletions test/IDBMirrorVFS.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { vfs_xAccess } from "./vfs_xAccess.js";
import { vfs_xClose } from "./vfs_xClose.js";
import { vfs_xRead } from "./vfs_xRead.js";
import { vfs_xWrite } from "./vfs_xWrite.js";
import { vfs_rollback } from "./vfs_rollback.js";

const CONFIG = 'IDBMirrorVFS';
const BUILDS = ['asyncify', 'jspi'];
Expand All @@ -22,6 +23,7 @@ describe(CONFIG, function() {
vfs_xClose(context);
vfs_xRead(context);
vfs_xWrite(context);
vfs_rollback(context);
});
}
});
47 changes: 47 additions & 0 deletions test/vfs_rollback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as Comlink from 'comlink';

/**
* A rollback of a transaction large enough to spill the page cache. SQLite
* then journals the pages it writes, and stamps the journal header once they
* are on disk; the rollback reads that header back to undo them.
* @param {import('./TestContext.js').TestContext} context
*/
export function vfs_rollback(context) {
describe('vfs_rollback', function() {
let proxy, sqlite3, db;
beforeEach(async function() {
proxy = await context.create();
sqlite3 = proxy.sqlite3;
db = await sqlite3.open_v2('rollback-test');
});

afterEach(async function() {
await sqlite3.close(db);
await context.destroy(proxy);
});

it('should undo a transaction that spilled the page cache', async function() {
await sqlite3.exec(db, 'CREATE TABLE t(x)');
await sqlite3.exec(db, "INSERT INTO t VALUES ('before')");

// Large enough that SQLite must write pages before the commit, which
// is what makes it journal them.
await sqlite3.exec(db, 'BEGIN');
await sqlite3.exec(db, `
INSERT INTO t WITH RECURSIVE c(x) AS
(SELECT 1 UNION ALL SELECT x + 1 FROM c WHERE x < 200000)
SELECT x FROM c`);
await sqlite3.exec(db, 'ROLLBACK');

const integrity = [];
await sqlite3.exec(db, 'PRAGMA integrity_check',
Comlink.proxy(row => { integrity.push(row[0]); }));
expect(integrity).toEqual(['ok']);

const rows = [];
await sqlite3.exec(db, 'SELECT x FROM t',
Comlink.proxy(row => { rows.push(row[0]); }));
expect(rows).toEqual(['before']);
});
});
}
Loading