mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Rename the DatabaseSync and StatementSync classes to Database and Statement, and the internal DatabaseSyncLimits helper to DatabaseLimits. The old names are kept as aliases of the new classes and are Documentation-only deprecated (DEP0210, DEP0211). Assisted-by: Claude Code Signed-off-by: Guilherme Araújo <arauujogui@gmail.com> PR-URL: https://github.com/nodejs/node/pull/65988 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
238 lines
7.4 KiB
JavaScript
238 lines
7.4 KiB
JavaScript
// Flags: --expose-gc
|
|
'use strict';
|
|
|
|
const { mustCall, skipIfSQLiteMissing } = require('../common');
|
|
skipIfSQLiteMissing();
|
|
|
|
const assert = require('node:assert');
|
|
const dc = require('node:diagnostics_channel');
|
|
const { Database } = require('node:sqlite');
|
|
const { suite, it } = require('node:test');
|
|
const { gcUntil } = require('../common/gc');
|
|
|
|
suite('sqlite.db.query diagnostics channel', () => {
|
|
it('subscriber receives SQL string for exec() statements', (t) => {
|
|
const calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
db.exec('INSERT INTO t VALUES (1)');
|
|
|
|
assert.strictEqual(calls.length, 2);
|
|
assert.strictEqual(calls[0].sql, 'CREATE TABLE t (x INTEGER)');
|
|
assert.strictEqual(calls[1].sql, 'INSERT INTO t VALUES (1)');
|
|
});
|
|
|
|
it('subscriber receives SQL string for prepared INSERT statements', (t) => {
|
|
let calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
calls = []; // reset after setup
|
|
|
|
using stmt = db.prepare('INSERT INTO t VALUES (?)');
|
|
stmt.run(42);
|
|
|
|
assert.strictEqual(calls.length, 1);
|
|
assert.strictEqual(calls[0].sql, 'INSERT INTO t VALUES (42.0)');
|
|
});
|
|
|
|
it('subscriber receives SQL string for prepared SELECT statements', (t) => {
|
|
let calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
db.exec('INSERT INTO t VALUES (1)');
|
|
calls = []; // reset after setup
|
|
|
|
using stmt = db.prepare('SELECT x FROM t WHERE x = ?');
|
|
stmt.get(1);
|
|
|
|
assert.strictEqual(calls.length, 1);
|
|
assert.strictEqual(calls[0].sql, 'SELECT x FROM t WHERE x = 1.0');
|
|
});
|
|
|
|
it('subscriber receives SQL string for prepared UPDATE statements', (t) => {
|
|
let calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
db.exec('INSERT INTO t VALUES (1)');
|
|
calls = []; // reset after setup
|
|
|
|
using stmt = db.prepare('UPDATE t SET x = ? WHERE x = ?');
|
|
stmt.run(2, 1);
|
|
|
|
assert.strictEqual(calls.length, 1);
|
|
assert.strictEqual(calls[0].sql, 'UPDATE t SET x = 2.0 WHERE x = 1.0');
|
|
});
|
|
|
|
it('subscriber receives SQL string for prepared DELETE statements', (t) => {
|
|
let calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
db.exec('INSERT INTO t VALUES (1)');
|
|
calls = []; // reset after setup
|
|
|
|
using stmt = db.prepare('DELETE FROM t WHERE x = ?');
|
|
stmt.run(1);
|
|
|
|
assert.strictEqual(calls.length, 1);
|
|
assert.strictEqual(calls[0].sql, 'DELETE FROM t WHERE x = 1.0');
|
|
});
|
|
|
|
it('no calls received after unsubscribe', (t) => {
|
|
const calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
assert.strictEqual(calls.length, 1);
|
|
|
|
dc.unsubscribe('sqlite.db.query', handler);
|
|
db.exec('INSERT INTO t VALUES (1)');
|
|
assert.strictEqual(calls.length, 1); // No new calls after unsubscribe
|
|
});
|
|
|
|
it('falls back to source SQL when expansion fails', (t) => {
|
|
let calls = [];
|
|
using db = new Database(':memory:', { limits: { length: 1000 } });
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x TEXT)');
|
|
calls = []; // reset after setup
|
|
|
|
using stmt = db.prepare('INSERT INTO t VALUES (?)');
|
|
|
|
const longValue = 'a'.repeat(977);
|
|
stmt.run(longValue);
|
|
|
|
assert.strictEqual(calls.length, 1);
|
|
// Falls back to source SQL with unexpanded '?' placeholder
|
|
assert.strictEqual(calls[0].sql, 'INSERT INTO t VALUES (?)');
|
|
});
|
|
|
|
it('database property identifies the correct database', (t) => {
|
|
const calls = [];
|
|
using db1 = new Database(':memory:');
|
|
using db2 = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db1.exec('CREATE TABLE t (x INTEGER)');
|
|
db2.exec('CREATE TABLE t (x INTEGER)');
|
|
|
|
assert.strictEqual(calls.length, 2);
|
|
assert.strictEqual(calls[0].database, db1);
|
|
assert.strictEqual(calls[1].database, db2);
|
|
assert.notStrictEqual(calls[0].database, calls[1].database);
|
|
});
|
|
|
|
it('duration is a number', (t) => {
|
|
const calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
|
|
assert.strictEqual(calls.length, 1);
|
|
assert.strictEqual(typeof calls[0].duration, 'number');
|
|
});
|
|
|
|
it('duration is non-negative', (t) => {
|
|
const calls = [];
|
|
using db = new Database(':memory:');
|
|
|
|
const handler = (msg) => calls.push(msg);
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
|
|
assert.strictEqual(calls.length, 1);
|
|
assert.ok(calls[0].duration >= 0);
|
|
});
|
|
|
|
it('does not publish when an unfinished statement is collected', async (t) => {
|
|
let calls = 0;
|
|
const handler = () => calls++;
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
let collected = false;
|
|
const registry = new FinalizationRegistry(() => { collected = true; });
|
|
|
|
(() => {
|
|
const db = new Database(':memory:');
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
for (let i = 0; i < 10; i++) {
|
|
db.exec(`INSERT INTO t VALUES (${i})`);
|
|
}
|
|
|
|
const stmt = db.prepare('SELECT x FROM t');
|
|
registry.register(stmt);
|
|
stmt.iterate().next(); // Leave the statement unfinished.
|
|
})();
|
|
|
|
calls = 0; // reset after setup
|
|
await gcUntil('unfinished statement is collected', () => collected);
|
|
|
|
assert.strictEqual(calls, 0);
|
|
});
|
|
|
|
it('subscriber cannot close the database or statement', (t) => {
|
|
using db = new Database(':memory:');
|
|
|
|
db.exec('CREATE TABLE t (x INTEGER)');
|
|
using stmt = db.prepare('INSERT INTO t VALUES (?)');
|
|
|
|
const handler = mustCall(() => {
|
|
assert.throws(() => db.close(), { code: 'ERR_INVALID_STATE' });
|
|
assert.throws(() => stmt.close(), { code: 'ERR_INVALID_STATE' });
|
|
assert.throws(() => stmt[Symbol.dispose](), {
|
|
code: 'ERR_INVALID_STATE',
|
|
});
|
|
});
|
|
dc.subscribe('sqlite.db.query', handler);
|
|
t.after(() => dc.unsubscribe('sqlite.db.query', handler));
|
|
|
|
stmt.run(1);
|
|
|
|
dc.unsubscribe('sqlite.db.query', handler);
|
|
assert.deepStrictEqual(db.prepare('SELECT x FROM t').all(), [
|
|
{ __proto__: null, x: 1 },
|
|
]);
|
|
});
|
|
});
|