diff --git a/benchmark/sqlite/sqlite-trace.js b/benchmark/sqlite/sqlite-trace.js new file mode 100644 index 00000000000000..86a0788abbe298 --- /dev/null +++ b/benchmark/sqlite/sqlite-trace.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common.js'); +const sqlite = require('node:sqlite'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const bench = common.createBenchmark(main, { + n: [1e5], + mode: ['none', 'subscribed', 'unsubscribed'], +}); + +function main(conf) { + const { n, mode } = conf; + + const db = new sqlite.DatabaseSync(':memory:'); + db.exec('CREATE TABLE t (x INTEGER)'); + const insert = db.prepare('INSERT INTO t VALUES (?)'); + + let subscriber; + if (mode === 'subscribed') { + subscriber = () => {}; + dc.subscribe('sqlite.db.query', subscriber); + } else if (mode === 'unsubscribed') { + subscriber = () => {}; + dc.subscribe('sqlite.db.query', subscriber); + dc.unsubscribe('sqlite.db.query', subscriber); + } + // mode === 'none': no subscription ever made + + let result; + bench.start(); + for (let i = 0; i < n; i++) { + result = insert.run(i); + } + bench.end(n); + + if (mode === 'subscribed') { + dc.unsubscribe('sqlite.db.query', subscriber); + } + + assert.ok(result !== undefined); +} diff --git a/doc/api/sqlite.md b/doc/api/sqlite.md index 35bbebea16e158..43400c17740d04 100644 --- a/doc/api/sqlite.md +++ b/doc/api/sqlite.md @@ -1281,6 +1281,66 @@ const totalPagesTransferred = await backup(sourceDb, 'backup.db', { console.log('Backup completed', totalPagesTransferred); ``` +## Diagnostics channel + + + +The `node:sqlite` module publishes SQL trace events on the +[`diagnostics_channel`][] channel `sqlite.db.query`. This allows subscribers +to observe every SQL statement executed against any `DatabaseSync` instance +without modifying the database code itself. Tracing is zero-cost when there +are no subscribers. + +### Channel `sqlite.db.query` + +The message published to this channel is a {string} containing the expanded +SQL with bound parameter values substituted. If expansion fails, the source +SQL with unsubstituted placeholders is used instead. + +```cjs +const dc = require('node:diagnostics_channel'); +const { DatabaseSync } = require('node:sqlite'); + +function onQuery(sql) { + console.log(sql); +} + +dc.subscribe('sqlite.db.query', onQuery); + +const db = new DatabaseSync(':memory:'); +db.exec('CREATE TABLE t (x INTEGER)'); +// Logs: CREATE TABLE t (x INTEGER) + +const stmt = db.prepare('INSERT INTO t VALUES (?)'); +stmt.run(42); +// Logs: INSERT INTO t VALUES (42.0) + +dc.unsubscribe('sqlite.db.query', onQuery); +``` + +```mjs +import dc from 'node:diagnostics_channel'; +import { DatabaseSync } from 'node:sqlite'; + +function onQuery(sql) { + console.log(sql); +} + +dc.subscribe('sqlite.db.query', onQuery); + +const db = new DatabaseSync(':memory:'); +db.exec('CREATE TABLE t (x INTEGER)'); +// Logs: CREATE TABLE t (x INTEGER) + +const stmt = db.prepare('INSERT INTO t VALUES (?)'); +stmt.run(42); +// Logs: INSERT INTO t VALUES (42.0) + +dc.unsubscribe('sqlite.db.query', onQuery); +``` + ## `sqlite.constants`