sqlite: split up large test file
The original test/parallel/test-sqlite.js test appears to time out in the CI occasionally. This commit splits the test into several smaller test files. Fixes: https://github.com/nodejs/node/issues/54006 PR-URL: https://github.com/nodejs/node/pull/54014 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
parent
717b4018f1
commit
916865ba8b
157
test/parallel/test-sqlite-data-types.js
Normal file
157
test/parallel/test-sqlite-data-types.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// Flags: --experimental-sqlite
|
||||||
|
'use strict';
|
||||||
|
require('../common');
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
const { join } = require('node:path');
|
||||||
|
const { DatabaseSync } = require('node:sqlite');
|
||||||
|
const { suite, test } = require('node:test');
|
||||||
|
let cnt = 0;
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
|
|
||||||
|
function nextDb() {
|
||||||
|
return join(tmpdir.path, `database-${cnt++}.db`);
|
||||||
|
}
|
||||||
|
|
||||||
|
suite('data binding and mapping', () => {
|
||||||
|
test('supported data types', (t) => {
|
||||||
|
const u8a = new TextEncoder().encode('a☃b☃c');
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(`
|
||||||
|
CREATE TABLE types(
|
||||||
|
key INTEGER PRIMARY KEY,
|
||||||
|
int INTEGER,
|
||||||
|
double REAL,
|
||||||
|
text TEXT,
|
||||||
|
buf BLOB
|
||||||
|
) STRICT;
|
||||||
|
`);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO types (key, int, double, text, buf) ' +
|
||||||
|
'VALUES (?, ?, ?, ?, ?)');
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run(1, 42, 3.14159, 'foo', u8a),
|
||||||
|
{ changes: 1, lastInsertRowid: 1 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run(2, null, null, null, null),
|
||||||
|
{ changes: 1, lastInsertRowid: 2 }
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run(3, Number(8), Number(2.718), String('bar'), Buffer.from('x☃y☃')),
|
||||||
|
{ changes: 1, lastInsertRowid: 3 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run(4, 99n, 0xf, '', new Uint8Array()),
|
||||||
|
{ changes: 1, lastInsertRowid: 4 },
|
||||||
|
);
|
||||||
|
|
||||||
|
const query = db.prepare('SELECT * FROM types WHERE key = ?');
|
||||||
|
t.assert.deepStrictEqual(query.get(1), {
|
||||||
|
key: 1,
|
||||||
|
int: 42,
|
||||||
|
double: 3.14159,
|
||||||
|
text: 'foo',
|
||||||
|
buf: u8a,
|
||||||
|
});
|
||||||
|
t.assert.deepStrictEqual(query.get(2), {
|
||||||
|
key: 2,
|
||||||
|
int: null,
|
||||||
|
double: null,
|
||||||
|
text: null,
|
||||||
|
buf: null,
|
||||||
|
});
|
||||||
|
t.assert.deepStrictEqual(query.get(3), {
|
||||||
|
key: 3,
|
||||||
|
int: 8,
|
||||||
|
double: 2.718,
|
||||||
|
text: 'bar',
|
||||||
|
buf: new TextEncoder().encode('x☃y☃'),
|
||||||
|
});
|
||||||
|
t.assert.deepStrictEqual(query.get(4), {
|
||||||
|
key: 4,
|
||||||
|
int: 99,
|
||||||
|
double: 0xf,
|
||||||
|
text: '',
|
||||||
|
buf: new Uint8Array(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unsupported data types', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
|
||||||
|
[
|
||||||
|
undefined,
|
||||||
|
() => {},
|
||||||
|
Symbol(),
|
||||||
|
/foo/,
|
||||||
|
Promise.resolve(),
|
||||||
|
new Map(),
|
||||||
|
new Set(),
|
||||||
|
].forEach((val) => {
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.prepare('INSERT INTO types (key, val) VALUES (?, ?)').run(1, val);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /Provided value cannot be bound to SQLite parameter 2/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, $v)');
|
||||||
|
stmt.run({ $k: 1, $v: () => {} });
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /Provided value cannot be bound to SQLite parameter 2/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when binding a BigInt that is too large', (t) => {
|
||||||
|
const max = 9223372036854775807n; // Largest 64-bit signed integer value.
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO types (key, val) VALUES (?, ?)');
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run(1, max),
|
||||||
|
{ changes: 1, lastInsertRowid: 1 },
|
||||||
|
);
|
||||||
|
t.assert.throws(() => {
|
||||||
|
stmt.run(1, max + 1n);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_VALUE',
|
||||||
|
message: /BigInt value is too large to bind/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('statements are unbound on each call', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)');
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run(1, 5),
|
||||||
|
{ changes: 1, lastInsertRowid: 1 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run(),
|
||||||
|
{ changes: 1, lastInsertRowid: 2 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('SELECT * FROM data ORDER BY key').all(),
|
||||||
|
[{ key: 1, val: 5 }, { key: 2, val: null }],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
185
test/parallel/test-sqlite-database-sync.js
Normal file
185
test/parallel/test-sqlite-database-sync.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Flags: --experimental-sqlite
|
||||||
|
'use strict';
|
||||||
|
require('../common');
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
const { existsSync } = require('node:fs');
|
||||||
|
const { join } = require('node:path');
|
||||||
|
const { DatabaseSync, StatementSync } = require('node:sqlite');
|
||||||
|
const { suite, test } = require('node:test');
|
||||||
|
let cnt = 0;
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
|
|
||||||
|
function nextDb() {
|
||||||
|
return join(tmpdir.path, `database-${cnt++}.db`);
|
||||||
|
}
|
||||||
|
|
||||||
|
suite('DatabaseSync() constructor', () => {
|
||||||
|
test('throws if called without new', (t) => {
|
||||||
|
t.assert.throws(() => {
|
||||||
|
DatabaseSync();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_CONSTRUCT_CALL_REQUIRED',
|
||||||
|
message: /Cannot call constructor without `new`/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if database path is not a string', (t) => {
|
||||||
|
t.assert.throws(() => {
|
||||||
|
new DatabaseSync();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /The "path" argument must be a string/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if options is provided but is not an object', (t) => {
|
||||||
|
t.assert.throws(() => {
|
||||||
|
new DatabaseSync('foo', null);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /The "options" argument must be an object/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if options.open is provided but is not a boolean', (t) => {
|
||||||
|
t.assert.throws(() => {
|
||||||
|
new DatabaseSync('foo', { open: 5 });
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /The "options\.open" argument must be a boolean/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('DatabaseSync.prototype.open()', () => {
|
||||||
|
test('opens a database connection', (t) => {
|
||||||
|
const dbPath = nextDb();
|
||||||
|
const db = new DatabaseSync(dbPath, { open: false });
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
|
||||||
|
t.assert.strictEqual(existsSync(dbPath), false);
|
||||||
|
t.assert.strictEqual(db.open(), undefined);
|
||||||
|
t.assert.strictEqual(existsSync(dbPath), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if database is already open', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb(), { open: false });
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
|
||||||
|
db.open();
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.open();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
message: /database is already open/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('DatabaseSync.prototype.close()', () => {
|
||||||
|
test('closes an open database connection', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
|
||||||
|
t.assert.strictEqual(db.close(), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if database is not open', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb(), { open: false });
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.close();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
message: /database is not open/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('DatabaseSync.prototype.prepare()', () => {
|
||||||
|
test('returns a prepared statement', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const stmt = db.prepare('CREATE TABLE webstorage(key TEXT)');
|
||||||
|
t.assert.ok(stmt instanceof StatementSync);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if database is not open', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb(), { open: false });
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.prepare();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
message: /database is not open/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if sql is not a string', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.prepare();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /The "sql" argument must be a string/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('DatabaseSync.prototype.exec()', () => {
|
||||||
|
test('executes SQL', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const result = db.exec(`
|
||||||
|
CREATE TABLE data(
|
||||||
|
key INTEGER PRIMARY KEY,
|
||||||
|
val INTEGER
|
||||||
|
) STRICT;
|
||||||
|
INSERT INTO data (key, val) VALUES (1, 2);
|
||||||
|
INSERT INTO data (key, val) VALUES (8, 9);
|
||||||
|
`);
|
||||||
|
t.assert.strictEqual(result, undefined);
|
||||||
|
const stmt = db.prepare('SELECT * FROM data ORDER BY key');
|
||||||
|
t.assert.deepStrictEqual(stmt.all(), [
|
||||||
|
{ key: 1, val: 2 },
|
||||||
|
{ key: 8, val: 9 },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reports errors from SQLite', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.exec('CREATE TABLEEEE');
|
||||||
|
}, {
|
||||||
|
code: 'ERR_SQLITE_ERROR',
|
||||||
|
message: /syntax error/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if database is not open', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb(), { open: false });
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.exec();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
message: /database is not open/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if sql is not a string', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
db.exec();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /The "sql" argument must be a string/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
80
test/parallel/test-sqlite-named-parameters.js
Normal file
80
test/parallel/test-sqlite-named-parameters.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Flags: --experimental-sqlite
|
||||||
|
'use strict';
|
||||||
|
require('../common');
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
const { join } = require('node:path');
|
||||||
|
const { DatabaseSync } = require('node:sqlite');
|
||||||
|
const { suite, test } = require('node:test');
|
||||||
|
let cnt = 0;
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
|
|
||||||
|
function nextDb() {
|
||||||
|
return join(tmpdir.path, `database-${cnt++}.db`);
|
||||||
|
}
|
||||||
|
|
||||||
|
suite('named parameters', () => {
|
||||||
|
test('throws on unknown named parameters', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
|
||||||
|
t.assert.throws(() => {
|
||||||
|
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, $v)');
|
||||||
|
stmt.run({ $k: 1, $unknown: 1 });
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
message: /Unknown named parameter '\$unknown'/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('bare named parameters are supported', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
|
||||||
|
stmt.run({ k: 1, v: 9 });
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('SELECT * FROM data').get(),
|
||||||
|
{ key: 1, val: 9 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('duplicate bare named parameters are supported', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $k)');
|
||||||
|
stmt.run({ k: 1 });
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('SELECT * FROM data').get(),
|
||||||
|
{ key: 1, val: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('bare named parameters throw on ambiguous names', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, @k)');
|
||||||
|
t.assert.throws(() => {
|
||||||
|
stmt.run({ k: 1 });
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
message: 'Cannot create bare named parameter \'k\' because of ' +
|
||||||
|
'conflicting names \'$k\' and \'@k\'.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
273
test/parallel/test-sqlite-statement-sync.js
Normal file
273
test/parallel/test-sqlite-statement-sync.js
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
// Flags: --experimental-sqlite
|
||||||
|
'use strict';
|
||||||
|
require('../common');
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
const { join } = require('node:path');
|
||||||
|
const { DatabaseSync, StatementSync } = require('node:sqlite');
|
||||||
|
const { suite, test } = require('node:test');
|
||||||
|
let cnt = 0;
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
|
|
||||||
|
function nextDb() {
|
||||||
|
return join(tmpdir.path, `database-${cnt++}.db`);
|
||||||
|
}
|
||||||
|
|
||||||
|
suite('StatementSync() constructor', () => {
|
||||||
|
test('StatementSync cannot be constructed directly', (t) => {
|
||||||
|
t.assert.throws(() => {
|
||||||
|
new StatementSync();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_ILLEGAL_CONSTRUCTOR',
|
||||||
|
message: /Illegal constructor/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('StatementSync.prototype.get()', () => {
|
||||||
|
test('executes a query and returns undefined on no results', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
||||||
|
t.assert.strictEqual(stmt.get(), undefined);
|
||||||
|
stmt = db.prepare('SELECT * FROM storage');
|
||||||
|
t.assert.strictEqual(stmt.get(), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('executes a query and returns the first result', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
||||||
|
t.assert.strictEqual(stmt.get(), undefined);
|
||||||
|
stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)');
|
||||||
|
t.assert.strictEqual(stmt.get('key1', 'val1'), undefined);
|
||||||
|
t.assert.strictEqual(stmt.get('key2', 'val2'), undefined);
|
||||||
|
stmt = db.prepare('SELECT * FROM storage ORDER BY key');
|
||||||
|
t.assert.deepStrictEqual(stmt.get(), { key: 'key1', val: 'val1' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('StatementSync.prototype.all()', () => {
|
||||||
|
test('executes a query and returns an empty array on no results', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
||||||
|
t.assert.deepStrictEqual(stmt.all(), []);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('executes a query and returns all results', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
||||||
|
t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 });
|
||||||
|
stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)');
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run('key1', 'val1'),
|
||||||
|
{ changes: 1, lastInsertRowid: 1 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run('key2', 'val2'),
|
||||||
|
{ changes: 1, lastInsertRowid: 2 },
|
||||||
|
);
|
||||||
|
stmt = db.prepare('SELECT * FROM storage ORDER BY key');
|
||||||
|
t.assert.deepStrictEqual(stmt.all(), [
|
||||||
|
{ key: 'key1', val: 'val1' },
|
||||||
|
{ key: 'key2', val: 'val2' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('StatementSync.prototype.run()', () => {
|
||||||
|
test('executes a query and returns change metadata', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(`
|
||||||
|
CREATE TABLE storage(key TEXT, val TEXT);
|
||||||
|
INSERT INTO storage (key, val) VALUES ('foo', 'bar');
|
||||||
|
`);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('SELECT * FROM storage');
|
||||||
|
t.assert.deepStrictEqual(stmt.run(), { changes: 1, lastInsertRowid: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SQLite throws when trying to bind too many parameters', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)');
|
||||||
|
t.assert.throws(() => {
|
||||||
|
stmt.run(1, 2, 3);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_SQLITE_ERROR',
|
||||||
|
message: 'column index out of range',
|
||||||
|
errcode: 25,
|
||||||
|
errstr: 'column index out of range',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SQLite defaults to NULL for unbound parameters', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)');
|
||||||
|
t.assert.throws(() => {
|
||||||
|
stmt.run(1);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_SQLITE_ERROR',
|
||||||
|
message: 'NOT NULL constraint failed: data.val',
|
||||||
|
errcode: 1299,
|
||||||
|
errstr: 'constraint failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('StatementSync.prototype.sourceSQL()', () => {
|
||||||
|
test('returns input SQL', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const sql = 'INSERT INTO types (key, val) VALUES ($k, $v)';
|
||||||
|
const stmt = db.prepare(sql);
|
||||||
|
t.assert.strictEqual(stmt.sourceSQL(), sql);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('StatementSync.prototype.expandedSQL()', () => {
|
||||||
|
test('returns expanded SQL', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const sql = 'INSERT INTO types (key, val) VALUES ($k, ?)';
|
||||||
|
const expanded = 'INSERT INTO types (key, val) VALUES (\'33\', \'42\')';
|
||||||
|
const stmt = db.prepare(sql);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run({ $k: '33' }, '42'),
|
||||||
|
{ changes: 1, lastInsertRowid: 33 },
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(stmt.expandedSQL(), expanded);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('StatementSync.prototype.setReadBigInts()', () => {
|
||||||
|
test('BigInts support can be toggled', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(`
|
||||||
|
CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;
|
||||||
|
INSERT INTO data (key, val) VALUES (1, 42);
|
||||||
|
`);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
|
||||||
|
const query = db.prepare('SELECT val FROM data');
|
||||||
|
t.assert.deepStrictEqual(query.get(), { val: 42 });
|
||||||
|
t.assert.strictEqual(query.setReadBigInts(true), undefined);
|
||||||
|
t.assert.deepStrictEqual(query.get(), { val: 42n });
|
||||||
|
t.assert.strictEqual(query.setReadBigInts(false), undefined);
|
||||||
|
t.assert.deepStrictEqual(query.get(), { val: 42 });
|
||||||
|
|
||||||
|
const insert = db.prepare('INSERT INTO data (key) VALUES (?)');
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
insert.run(10),
|
||||||
|
{ changes: 1, lastInsertRowid: 10 },
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(insert.setReadBigInts(true), undefined);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
insert.run(20),
|
||||||
|
{ changes: 1n, lastInsertRowid: 20n },
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(insert.setReadBigInts(false), undefined);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
insert.run(30),
|
||||||
|
{ changes: 1, lastInsertRowid: 30 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when input is not a boolean', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, $v)');
|
||||||
|
t.assert.throws(() => {
|
||||||
|
stmt.setReadBigInts();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /The "readBigInts" argument must be a boolean/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('BigInt is required for reading large integers', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const bad = db.prepare(`SELECT ${Number.MAX_SAFE_INTEGER} + 1`);
|
||||||
|
t.assert.throws(() => {
|
||||||
|
bad.get();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_OUT_OF_RANGE',
|
||||||
|
message: /^The value of column 0 is too large.*: 9007199254740992$/,
|
||||||
|
});
|
||||||
|
const good = db.prepare(`SELECT ${Number.MAX_SAFE_INTEGER} + 1`);
|
||||||
|
good.setReadBigInts(true);
|
||||||
|
t.assert.deepStrictEqual(good.get(), {
|
||||||
|
[`${Number.MAX_SAFE_INTEGER} + 1`]: 2n ** 53n,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('StatementSync.prototype.setAllowBareNamedParameters()', () => {
|
||||||
|
test('bare named parameter support can be toggled', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run({ k: 1, v: 2 }),
|
||||||
|
{ changes: 1, lastInsertRowid: 1 },
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(stmt.setAllowBareNamedParameters(false), undefined);
|
||||||
|
t.assert.throws(() => {
|
||||||
|
stmt.run({ k: 2, v: 4 });
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
message: /Unknown named parameter 'k'/,
|
||||||
|
});
|
||||||
|
t.assert.strictEqual(stmt.setAllowBareNamedParameters(true), undefined);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
stmt.run({ k: 3, v: 6 }),
|
||||||
|
{ changes: 1, lastInsertRowid: 3 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when input is not a boolean', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(
|
||||||
|
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
||||||
|
);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
|
||||||
|
t.assert.throws(() => {
|
||||||
|
stmt.setAllowBareNamedParameters();
|
||||||
|
}, {
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: /The "allowBareNamedParameters" argument must be a boolean/,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
67
test/parallel/test-sqlite-transactions.js
Normal file
67
test/parallel/test-sqlite-transactions.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Flags: --experimental-sqlite
|
||||||
|
'use strict';
|
||||||
|
require('../common');
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
|
const { join } = require('node:path');
|
||||||
|
const { DatabaseSync } = require('node:sqlite');
|
||||||
|
const { suite, test } = require('node:test');
|
||||||
|
let cnt = 0;
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
|
|
||||||
|
function nextDb() {
|
||||||
|
return join(tmpdir.path, `database-${cnt++}.db`);
|
||||||
|
}
|
||||||
|
|
||||||
|
suite('manual transactions', () => {
|
||||||
|
test('a transaction is committed', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(`
|
||||||
|
CREATE TABLE data(
|
||||||
|
key INTEGER PRIMARY KEY
|
||||||
|
) STRICT;
|
||||||
|
`);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('BEGIN').run(),
|
||||||
|
{ changes: 0, lastInsertRowid: 0 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('INSERT INTO data (key) VALUES (100)').run(),
|
||||||
|
{ changes: 1, lastInsertRowid: 100 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('COMMIT').run(),
|
||||||
|
{ changes: 1, lastInsertRowid: 100 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('SELECT * FROM data').all(),
|
||||||
|
[{ key: 100 }],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a transaction is rolled back', (t) => {
|
||||||
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
|
const setup = db.exec(`
|
||||||
|
CREATE TABLE data(
|
||||||
|
key INTEGER PRIMARY KEY
|
||||||
|
) STRICT;
|
||||||
|
`);
|
||||||
|
t.assert.strictEqual(setup, undefined);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('BEGIN').run(),
|
||||||
|
{ changes: 0, lastInsertRowid: 0 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('INSERT INTO data (key) VALUES (100)').run(),
|
||||||
|
{ changes: 1, lastInsertRowid: 100 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(
|
||||||
|
db.prepare('ROLLBACK').run(),
|
||||||
|
{ changes: 1, lastInsertRowid: 100 },
|
||||||
|
);
|
||||||
|
t.assert.deepStrictEqual(db.prepare('SELECT * FROM data').all(), []);
|
||||||
|
});
|
||||||
|
});
|
@ -2,9 +2,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const { spawnPromisified } = require('../common');
|
const { spawnPromisified } = require('../common');
|
||||||
const tmpdir = require('../common/tmpdir');
|
const tmpdir = require('../common/tmpdir');
|
||||||
const { existsSync } = require('node:fs');
|
|
||||||
const { join } = require('node:path');
|
const { join } = require('node:path');
|
||||||
const { DatabaseSync, StatementSync } = require('node:sqlite');
|
const { DatabaseSync } = require('node:sqlite');
|
||||||
const { suite, test } = require('node:test');
|
const { suite, test } = require('node:test');
|
||||||
let cnt = 0;
|
let cnt = 0;
|
||||||
|
|
||||||
@ -42,668 +41,9 @@ suite('accessing the node:sqlite module', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('DatabaseSync() constructor', () => {
|
|
||||||
test('throws if called without new', (t) => {
|
|
||||||
t.assert.throws(() => {
|
|
||||||
DatabaseSync();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_CONSTRUCT_CALL_REQUIRED',
|
|
||||||
message: /Cannot call constructor without `new`/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if database path is not a string', (t) => {
|
|
||||||
t.assert.throws(() => {
|
|
||||||
new DatabaseSync();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /The "path" argument must be a string/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if options is provided but is not an object', (t) => {
|
|
||||||
t.assert.throws(() => {
|
|
||||||
new DatabaseSync('foo', null);
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /The "options" argument must be an object/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if options.open is provided but is not a boolean', (t) => {
|
|
||||||
t.assert.throws(() => {
|
|
||||||
new DatabaseSync('foo', { open: 5 });
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /The "options\.open" argument must be a boolean/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('DatabaseSync.prototype.open()', () => {
|
|
||||||
test('opens a database connection', (t) => {
|
|
||||||
const dbPath = nextDb();
|
|
||||||
const db = new DatabaseSync(dbPath, { open: false });
|
|
||||||
|
|
||||||
t.assert.strictEqual(existsSync(dbPath), false);
|
|
||||||
t.assert.strictEqual(db.open(), undefined);
|
|
||||||
t.assert.strictEqual(existsSync(dbPath), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if database is already open', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb(), { open: false });
|
|
||||||
|
|
||||||
db.open();
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.open();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_STATE',
|
|
||||||
message: /database is already open/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('DatabaseSync.prototype.close()', () => {
|
|
||||||
test('closes an open database connection', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
|
|
||||||
t.assert.strictEqual(db.close(), undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if database is not open', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb(), { open: false });
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.close();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_STATE',
|
|
||||||
message: /database is not open/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('DatabaseSync.prototype.prepare()', () => {
|
|
||||||
test('returns a prepared statement', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const stmt = db.prepare('CREATE TABLE webstorage(key TEXT)');
|
|
||||||
t.assert.ok(stmt instanceof StatementSync);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if database is not open', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb(), { open: false });
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.prepare();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_STATE',
|
|
||||||
message: /database is not open/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if sql is not a string', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.prepare();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /The "sql" argument must be a string/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('DatabaseSync.prototype.exec()', () => {
|
|
||||||
test('executes SQL', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const result = db.exec(`
|
|
||||||
CREATE TABLE data(
|
|
||||||
key INTEGER PRIMARY KEY,
|
|
||||||
val INTEGER
|
|
||||||
) STRICT;
|
|
||||||
INSERT INTO data (key, val) VALUES (1, 2);
|
|
||||||
INSERT INTO data (key, val) VALUES (8, 9);
|
|
||||||
`);
|
|
||||||
t.assert.strictEqual(result, undefined);
|
|
||||||
const stmt = db.prepare('SELECT * FROM data ORDER BY key');
|
|
||||||
t.assert.deepStrictEqual(stmt.all(), [
|
|
||||||
{ key: 1, val: 2 },
|
|
||||||
{ key: 8, val: 9 },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('reports errors from SQLite', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.exec('CREATE TABLEEEE');
|
|
||||||
}, {
|
|
||||||
code: 'ERR_SQLITE_ERROR',
|
|
||||||
message: /syntax error/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if database is not open', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb(), { open: false });
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.exec();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_STATE',
|
|
||||||
message: /database is not open/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if sql is not a string', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.exec();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /The "sql" argument must be a string/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync() constructor', () => {
|
|
||||||
test('StatementSync cannot be constructed directly', (t) => {
|
|
||||||
t.assert.throws(() => {
|
|
||||||
new StatementSync();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_ILLEGAL_CONSTRUCTOR',
|
|
||||||
message: /Illegal constructor/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync.prototype.get()', () => {
|
|
||||||
test('executes a query and returns undefined on no results', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
|
||||||
t.assert.strictEqual(stmt.get(), undefined);
|
|
||||||
stmt = db.prepare('SELECT * FROM storage');
|
|
||||||
t.assert.strictEqual(stmt.get(), undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('executes a query and returns the first result', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
|
||||||
t.assert.strictEqual(stmt.get(), undefined);
|
|
||||||
stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)');
|
|
||||||
t.assert.strictEqual(stmt.get('key1', 'val1'), undefined);
|
|
||||||
t.assert.strictEqual(stmt.get('key2', 'val2'), undefined);
|
|
||||||
stmt = db.prepare('SELECT * FROM storage ORDER BY key');
|
|
||||||
t.assert.deepStrictEqual(stmt.get(), { key: 'key1', val: 'val1' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync.prototype.all()', () => {
|
|
||||||
test('executes a query and returns an empty array on no results', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
|
||||||
t.assert.deepStrictEqual(stmt.all(), []);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('executes a query and returns all results', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
|
|
||||||
t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 });
|
|
||||||
stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)');
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run('key1', 'val1'),
|
|
||||||
{ changes: 1, lastInsertRowid: 1 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run('key2', 'val2'),
|
|
||||||
{ changes: 1, lastInsertRowid: 2 },
|
|
||||||
);
|
|
||||||
stmt = db.prepare('SELECT * FROM storage ORDER BY key');
|
|
||||||
t.assert.deepStrictEqual(stmt.all(), [
|
|
||||||
{ key: 'key1', val: 'val1' },
|
|
||||||
{ key: 'key2', val: 'val2' },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync.prototype.run()', () => {
|
|
||||||
test('executes a query and returns change metadata', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(`
|
|
||||||
CREATE TABLE storage(key TEXT, val TEXT);
|
|
||||||
INSERT INTO storage (key, val) VALUES ('foo', 'bar');
|
|
||||||
`);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('SELECT * FROM storage');
|
|
||||||
t.assert.deepStrictEqual(stmt.run(), { changes: 1, lastInsertRowid: 1 });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('SQLite throws when trying to bind too many parameters', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)');
|
|
||||||
t.assert.throws(() => {
|
|
||||||
stmt.run(1, 2, 3);
|
|
||||||
}, {
|
|
||||||
code: 'ERR_SQLITE_ERROR',
|
|
||||||
message: 'column index out of range',
|
|
||||||
errcode: 25,
|
|
||||||
errstr: 'column index out of range',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('SQLite defaults to NULL for unbound parameters', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)');
|
|
||||||
t.assert.throws(() => {
|
|
||||||
stmt.run(1);
|
|
||||||
}, {
|
|
||||||
code: 'ERR_SQLITE_ERROR',
|
|
||||||
message: 'NOT NULL constraint failed: data.val',
|
|
||||||
errcode: 1299,
|
|
||||||
errstr: 'constraint failed',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync.prototype.sourceSQL()', () => {
|
|
||||||
test('returns input SQL', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const sql = 'INSERT INTO types (key, val) VALUES ($k, $v)';
|
|
||||||
const stmt = db.prepare(sql);
|
|
||||||
t.assert.strictEqual(stmt.sourceSQL(), sql);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync.prototype.expandedSQL()', () => {
|
|
||||||
test('returns expanded SQL', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const sql = 'INSERT INTO types (key, val) VALUES ($k, ?)';
|
|
||||||
const expanded = 'INSERT INTO types (key, val) VALUES (\'33\', \'42\')';
|
|
||||||
const stmt = db.prepare(sql);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run({ $k: '33' }, '42'),
|
|
||||||
{ changes: 1, lastInsertRowid: 33 },
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(stmt.expandedSQL(), expanded);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync.prototype.setReadBigInts()', () => {
|
|
||||||
test('BigInts support can be toggled', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(`
|
|
||||||
CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;
|
|
||||||
INSERT INTO data (key, val) VALUES (1, 42);
|
|
||||||
`);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
|
|
||||||
const query = db.prepare('SELECT val FROM data');
|
|
||||||
t.assert.deepStrictEqual(query.get(), { val: 42 });
|
|
||||||
t.assert.strictEqual(query.setReadBigInts(true), undefined);
|
|
||||||
t.assert.deepStrictEqual(query.get(), { val: 42n });
|
|
||||||
t.assert.strictEqual(query.setReadBigInts(false), undefined);
|
|
||||||
t.assert.deepStrictEqual(query.get(), { val: 42 });
|
|
||||||
|
|
||||||
const insert = db.prepare('INSERT INTO data (key) VALUES (?)');
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
insert.run(10),
|
|
||||||
{ changes: 1, lastInsertRowid: 10 },
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(insert.setReadBigInts(true), undefined);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
insert.run(20),
|
|
||||||
{ changes: 1n, lastInsertRowid: 20n },
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(insert.setReadBigInts(false), undefined);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
insert.run(30),
|
|
||||||
{ changes: 1, lastInsertRowid: 30 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws when input is not a boolean', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, $v)');
|
|
||||||
t.assert.throws(() => {
|
|
||||||
stmt.setReadBigInts();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /The "readBigInts" argument must be a boolean/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('BigInt is required for reading large integers', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const bad = db.prepare(`SELECT ${Number.MAX_SAFE_INTEGER} + 1`);
|
|
||||||
t.assert.throws(() => {
|
|
||||||
bad.get();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_OUT_OF_RANGE',
|
|
||||||
message: /^The value of column 0 is too large.*: 9007199254740992$/,
|
|
||||||
});
|
|
||||||
const good = db.prepare(`SELECT ${Number.MAX_SAFE_INTEGER} + 1`);
|
|
||||||
good.setReadBigInts(true);
|
|
||||||
t.assert.deepStrictEqual(good.get(), {
|
|
||||||
[`${Number.MAX_SAFE_INTEGER} + 1`]: 2n ** 53n,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('StatementSync.prototype.setAllowBareNamedParameters()', () => {
|
|
||||||
test('bare named parameter support can be toggled', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run({ k: 1, v: 2 }),
|
|
||||||
{ changes: 1, lastInsertRowid: 1 },
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(stmt.setAllowBareNamedParameters(false), undefined);
|
|
||||||
t.assert.throws(() => {
|
|
||||||
stmt.run({ k: 2, v: 4 });
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_STATE',
|
|
||||||
message: /Unknown named parameter 'k'/,
|
|
||||||
});
|
|
||||||
t.assert.strictEqual(stmt.setAllowBareNamedParameters(true), undefined);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run({ k: 3, v: 6 }),
|
|
||||||
{ changes: 1, lastInsertRowid: 3 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws when input is not a boolean', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
|
|
||||||
t.assert.throws(() => {
|
|
||||||
stmt.setAllowBareNamedParameters();
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /The "allowBareNamedParameters" argument must be a boolean/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('data binding and mapping', () => {
|
|
||||||
test('supported data types', (t) => {
|
|
||||||
const u8a = new TextEncoder().encode('a☃b☃c');
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(`
|
|
||||||
CREATE TABLE types(
|
|
||||||
key INTEGER PRIMARY KEY,
|
|
||||||
int INTEGER,
|
|
||||||
double REAL,
|
|
||||||
text TEXT,
|
|
||||||
buf BLOB
|
|
||||||
) STRICT;
|
|
||||||
`);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO types (key, int, double, text, buf) ' +
|
|
||||||
'VALUES (?, ?, ?, ?, ?)');
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run(1, 42, 3.14159, 'foo', u8a),
|
|
||||||
{ changes: 1, lastInsertRowid: 1 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run(2, null, null, null, null),
|
|
||||||
{ changes: 1, lastInsertRowid: 2 }
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run(3, Number(8), Number(2.718), String('bar'), Buffer.from('x☃y☃')),
|
|
||||||
{ changes: 1, lastInsertRowid: 3 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run(4, 99n, 0xf, '', new Uint8Array()),
|
|
||||||
{ changes: 1, lastInsertRowid: 4 },
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = db.prepare('SELECT * FROM types WHERE key = ?');
|
|
||||||
t.assert.deepStrictEqual(query.get(1), {
|
|
||||||
key: 1,
|
|
||||||
int: 42,
|
|
||||||
double: 3.14159,
|
|
||||||
text: 'foo',
|
|
||||||
buf: u8a,
|
|
||||||
});
|
|
||||||
t.assert.deepStrictEqual(query.get(2), {
|
|
||||||
key: 2,
|
|
||||||
int: null,
|
|
||||||
double: null,
|
|
||||||
text: null,
|
|
||||||
buf: null,
|
|
||||||
});
|
|
||||||
t.assert.deepStrictEqual(query.get(3), {
|
|
||||||
key: 3,
|
|
||||||
int: 8,
|
|
||||||
double: 2.718,
|
|
||||||
text: 'bar',
|
|
||||||
buf: new TextEncoder().encode('x☃y☃'),
|
|
||||||
});
|
|
||||||
t.assert.deepStrictEqual(query.get(4), {
|
|
||||||
key: 4,
|
|
||||||
int: 99,
|
|
||||||
double: 0xf,
|
|
||||||
text: '',
|
|
||||||
buf: new Uint8Array(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('unsupported data types', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
|
|
||||||
[
|
|
||||||
undefined,
|
|
||||||
() => {},
|
|
||||||
Symbol(),
|
|
||||||
/foo/,
|
|
||||||
Promise.resolve(),
|
|
||||||
new Map(),
|
|
||||||
new Set(),
|
|
||||||
].forEach((val) => {
|
|
||||||
t.assert.throws(() => {
|
|
||||||
db.prepare('INSERT INTO types (key, val) VALUES (?, ?)').run(1, val);
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /Provided value cannot be bound to SQLite parameter 2/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, $v)');
|
|
||||||
stmt.run({ $k: 1, $v: () => {} });
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
|
||||||
message: /Provided value cannot be bound to SQLite parameter 2/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws when binding a BigInt that is too large', (t) => {
|
|
||||||
const max = 9223372036854775807n; // Largest 64-bit signed integer value.
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO types (key, val) VALUES (?, ?)');
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run(1, max),
|
|
||||||
{ changes: 1, lastInsertRowid: 1 },
|
|
||||||
);
|
|
||||||
t.assert.throws(() => {
|
|
||||||
stmt.run(1, max + 1n);
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_ARG_VALUE',
|
|
||||||
message: /BigInt value is too large to bind/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('statements are unbound on each call', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)');
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run(1, 5),
|
|
||||||
{ changes: 1, lastInsertRowid: 1 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
stmt.run(),
|
|
||||||
{ changes: 1, lastInsertRowid: 2 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('SELECT * FROM data ORDER BY key').all(),
|
|
||||||
[{ key: 1, val: 5 }, { key: 2, val: null }],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('manual transactions', () => {
|
|
||||||
test('a transaction is committed', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(`
|
|
||||||
CREATE TABLE data(
|
|
||||||
key INTEGER PRIMARY KEY
|
|
||||||
) STRICT;
|
|
||||||
`);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('BEGIN').run(),
|
|
||||||
{ changes: 0, lastInsertRowid: 0 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('INSERT INTO data (key) VALUES (100)').run(),
|
|
||||||
{ changes: 1, lastInsertRowid: 100 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('COMMIT').run(),
|
|
||||||
{ changes: 1, lastInsertRowid: 100 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('SELECT * FROM data').all(),
|
|
||||||
[{ key: 100 }],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a transaction is rolled back', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(`
|
|
||||||
CREATE TABLE data(
|
|
||||||
key INTEGER PRIMARY KEY
|
|
||||||
) STRICT;
|
|
||||||
`);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('BEGIN').run(),
|
|
||||||
{ changes: 0, lastInsertRowid: 0 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('INSERT INTO data (key) VALUES (100)').run(),
|
|
||||||
{ changes: 1, lastInsertRowid: 100 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('ROLLBACK').run(),
|
|
||||||
{ changes: 1, lastInsertRowid: 100 },
|
|
||||||
);
|
|
||||||
t.assert.deepStrictEqual(db.prepare('SELECT * FROM data').all(), []);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('named parameters', () => {
|
|
||||||
test('throws on unknown named parameters', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
|
|
||||||
t.assert.throws(() => {
|
|
||||||
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, $v)');
|
|
||||||
stmt.run({ $k: 1, $unknown: 1 });
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_STATE',
|
|
||||||
message: /Unknown named parameter '\$unknown'/,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('bare named parameters are supported', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
|
|
||||||
stmt.run({ k: 1, v: 9 });
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('SELECT * FROM data').get(),
|
|
||||||
{ key: 1, val: 9 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('duplicate bare named parameters are supported', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $k)');
|
|
||||||
stmt.run({ k: 1 });
|
|
||||||
t.assert.deepStrictEqual(
|
|
||||||
db.prepare('SELECT * FROM data').get(),
|
|
||||||
{ key: 1, val: 1 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('bare named parameters throw on ambiguous names', (t) => {
|
|
||||||
const db = new DatabaseSync(nextDb());
|
|
||||||
const setup = db.exec(
|
|
||||||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
|
|
||||||
);
|
|
||||||
t.assert.strictEqual(setup, undefined);
|
|
||||||
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, @k)');
|
|
||||||
t.assert.throws(() => {
|
|
||||||
stmt.run({ k: 1 });
|
|
||||||
}, {
|
|
||||||
code: 'ERR_INVALID_STATE',
|
|
||||||
message: 'Cannot create bare named parameter \'k\' because of ' +
|
|
||||||
'conflicting names \'$k\' and \'@k\'.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ERR_SQLITE_ERROR is thrown for errors originating from SQLite', (t) => {
|
test('ERR_SQLITE_ERROR is thrown for errors originating from SQLite', (t) => {
|
||||||
const db = new DatabaseSync(nextDb());
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
const setup = db.exec(`
|
const setup = db.exec(`
|
||||||
CREATE TABLE test(
|
CREATE TABLE test(
|
||||||
key INTEGER PRIMARY KEY
|
key INTEGER PRIMARY KEY
|
||||||
@ -747,6 +87,7 @@ test('in-memory databases are supported', (t) => {
|
|||||||
|
|
||||||
test('PRAGMAs are supported', (t) => {
|
test('PRAGMAs are supported', (t) => {
|
||||||
const db = new DatabaseSync(nextDb());
|
const db = new DatabaseSync(nextDb());
|
||||||
|
t.after(() => { db.close(); });
|
||||||
t.assert.deepStrictEqual(
|
t.assert.deepStrictEqual(
|
||||||
db.prepare('PRAGMA journal_mode = WAL').get(),
|
db.prepare('PRAGMA journal_mode = WAL').get(),
|
||||||
{ journal_mode: 'wal' },
|
{ journal_mode: 'wal' },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user