Files
node/test/parallel/test-sqlite-diagnostic-channel.js
Guilherme Araújo f2b698f1cf sqlite: rename DatabaseSync and StatementSync
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>
2026-09-26 00:03:45 +00:00

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 },
]);
});
});