Skip to content

Commit b68b4be

Browse files
committed
sqlite: rename to setSqlTraceHook
1 parent 0e8bfa9 commit b68b4be

File tree

5 files changed

+134
-72
lines changed

5 files changed

+134
-72
lines changed

doc/api/sqlite.md

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,6 @@ exposed by this class execute synchronously.
107107
<!-- YAML
108108
added: v22.5.0
109109
changes:
110-
- version: REPLACEME
111-
pr-url: https://github.com/nodejs/node/pull/62241
112-
description: Add `trace` option.
113110
- version:
114111
- v25.5.0
115112
- v24.14.0
@@ -183,13 +180,6 @@ changes:
183180
* `likePatternLength` {number} Maximum length of a LIKE pattern.
184181
* `variableNumber` {number} Maximum number of SQL variables.
185182
* `triggerDepth` {number} Maximum trigger recursion depth.
186-
* `trace` {Function} An optional callback function that is invoked for
187-
every SQL statement executed against the database. The callback receives
188-
the expanded SQL string (with bound parameter values substituted) as its
189-
only argument. If expansion fails, the source SQL (with unsubstituted
190-
placeholders) is passed instead. This is useful for logging and debugging.
191-
This option is a wrapper around [`sqlite3_trace_v2()`][].
192-
**Default:** `undefined`.
193183

194184
Constructs a new `DatabaseSync` instance.
195185

@@ -382,6 +372,56 @@ added:
382372
This method is used to create SQLite user-defined functions. This method is a
383373
wrapper around [`sqlite3_create_function_v2()`][].
384374

375+
### `database.setSqlTraceHook(hook)`
376+
377+
<!-- YAML
378+
added: REPLACEME
379+
-->
380+
381+
* `hook` {Function|null} The trace function to set, or `null` to clear
382+
the current hook.
383+
384+
Sets a hook that SQLite invokes for every SQL statement executed against the
385+
database. The hook receives the expanded SQL string (with bound parameter
386+
values substituted) as its only argument. If expansion fails, the source SQL
387+
with unsubstituted placeholders is passed instead.
388+
389+
This method is a wrapper around [`sqlite3_trace_v2()`][].
390+
391+
```cjs
392+
const { DatabaseSync } = require('node:sqlite');
393+
const db = new DatabaseSync(':memory:');
394+
395+
db.setSqlTraceHook((sql) => console.log(sql));
396+
397+
db.exec('CREATE TABLE t (x INTEGER)');
398+
// Logs: CREATE TABLE t (x INTEGER)
399+
400+
const stmt = db.prepare('INSERT INTO t VALUES (?)');
401+
stmt.run(42);
402+
// Logs: INSERT INTO t VALUES (42.0)
403+
404+
// Clear the hook
405+
db.setSqlTraceHook(null);
406+
```
407+
408+
```mjs
409+
import { DatabaseSync } from 'node:sqlite';
410+
const db = new DatabaseSync(':memory:');
411+
412+
db.setSqlTraceHook((sql) => console.log(sql));
413+
414+
db.exec('CREATE TABLE t (x INTEGER)');
415+
// Logs: CREATE TABLE t (x INTEGER)
416+
417+
const stmt = db.prepare('INSERT INTO t VALUES (?)');
418+
stmt.run(42);
419+
// Logs: INSERT INTO t VALUES (42.0)
420+
421+
// Clear the hook
422+
db.setSqlTraceHook(null);
423+
```
424+
385425
### `database.setAuthorizer(callback)`
386426

387427
<!-- YAML

src/env_properties.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@
370370
V(url_string, "url") \
371371
V(username_string, "username") \
372372
V(value_string, "value") \
373-
V(trace_string, "trace") \
374373
V(verify_error_string, "verifyError") \
375374
V(version_string, "version") \
376375
V(windows_hide_string, "windowsHide") \

src/node_sqlite.cc

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -974,15 +974,6 @@ bool DatabaseSync::Open() {
974974
env()->isolate(), this, load_extension_ret, SQLITE_OK, false);
975975
}
976976

977-
{
978-
Local<Value> cb =
979-
object()->GetInternalField(kTraceCallback).template As<Value>();
980-
if (cb->IsFunction()) {
981-
sqlite3_trace_v2(
982-
connection_, SQLITE_TRACE_STMT, DatabaseSync::TraceCallback, this);
983-
}
984-
}
985-
986977
return true;
987978
}
988979

@@ -1348,21 +1339,6 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {
13481339
}
13491340
}
13501341
}
1351-
1352-
// Parse trace option
1353-
Local<Value> trace_v;
1354-
if (!options->Get(env->context(), env->trace_string()).ToLocal(&trace_v)) {
1355-
return;
1356-
}
1357-
if (!trace_v->IsUndefined() && !trace_v->IsNull()) {
1358-
if (!trace_v->IsFunction()) {
1359-
THROW_ERR_INVALID_ARG_TYPE(
1360-
env->isolate(),
1361-
"The \"options.trace\" argument must be a function.");
1362-
return;
1363-
}
1364-
args.This()->SetInternalField(kTraceCallback, trace_v.As<Function>());
1365-
}
13661342
}
13671343

13681344
new DatabaseSync(
@@ -2309,6 +2285,30 @@ void DatabaseSync::LoadExtension(const FunctionCallbackInfo<Value>& args) {
23092285
}
23102286
}
23112287

2288+
void DatabaseSync::SetSqlTraceHook(const FunctionCallbackInfo<Value>& args) {
2289+
DatabaseSync* db;
2290+
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
2291+
Environment* env = Environment::GetCurrent(args);
2292+
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
2293+
Isolate* isolate = env->isolate();
2294+
2295+
if (args[0]->IsNull() || args[0]->IsUndefined()) {
2296+
sqlite3_trace_v2(db->connection_, 0, nullptr, nullptr);
2297+
db->object()->SetInternalField(kTraceCallback, Null(isolate));
2298+
return;
2299+
}
2300+
2301+
if (!args[0]->IsFunction()) {
2302+
THROW_ERR_INVALID_ARG_TYPE(isolate,
2303+
"The \"hook\" argument must be a function.");
2304+
return;
2305+
}
2306+
2307+
db->object()->SetInternalField(kTraceCallback, args[0].As<Function>());
2308+
sqlite3_trace_v2(
2309+
db->connection_, SQLITE_TRACE_STMT, DatabaseSync::TraceCallback, db);
2310+
}
2311+
23122312
void DatabaseSync::SetAuthorizer(const FunctionCallbackInfo<Value>& args) {
23132313
DatabaseSync* db;
23142314
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -3838,6 +3838,8 @@ static void Initialize(Local<Object> target,
38383838
isolate, db_tmpl, "enableDefensive", DatabaseSync::EnableDefensive);
38393839
SetProtoMethod(
38403840
isolate, db_tmpl, "loadExtension", DatabaseSync::LoadExtension);
3841+
SetProtoMethod(
3842+
isolate, db_tmpl, "setSqlTraceHook", DatabaseSync::SetSqlTraceHook);
38413843
SetProtoMethod(
38423844
isolate, db_tmpl, "setAuthorizer", DatabaseSync::SetAuthorizer);
38433845
SetSideEffectFreeGetter(isolate,

src/node_sqlite.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ class DatabaseSync : public BaseObject {
203203
const char* param2,
204204
const char* param3,
205205
const char* param4);
206+
static void SetSqlTraceHook(const v8::FunctionCallbackInfo<v8::Value>& args);
206207
static int TraceCallback(unsigned int type,
207208
void* user_data,
208209
void* p,

test/parallel/test-sqlite-trace.js

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,26 @@ const assert = require('node:assert');
77
const { DatabaseSync } = require('node:sqlite');
88
const { suite, it } = require('node:test');
99

10-
suite('DatabaseSync trace option', () => {
10+
suite('DatabaseSync.prototype.setSqlTraceHook()', () => {
1111
it('callback receives SQL string for exec() statements', (t) => {
1212
const calls = [];
13-
const db = new DatabaseSync(':memory:', {
14-
trace: (sql) => calls.push(sql),
15-
});
13+
const db = new DatabaseSync(':memory:');
14+
t.after(() => db.close());
15+
db.setSqlTraceHook((sql) => calls.push(sql));
1616

1717
db.exec('CREATE TABLE t (x INTEGER)');
1818
db.exec('INSERT INTO t VALUES (1)');
1919

2020
assert.strictEqual(calls.length, 2);
2121
assert.strictEqual(calls[0], 'CREATE TABLE t (x INTEGER)');
2222
assert.strictEqual(calls[1], 'INSERT INTO t VALUES (1)');
23-
db.close();
2423
});
2524

26-
it('callback receives SQL string for prepared statement execution', (t) => {
25+
it('callback receives SQL string for prepared INSERT statements', (t) => {
2726
let calls = [];
28-
const db = new DatabaseSync(':memory:', {
29-
trace: (sql) => calls.push(sql),
30-
});
27+
const db = new DatabaseSync(':memory:');
28+
t.after(() => db.close());
29+
db.setSqlTraceHook((sql) => calls.push(sql));
3130

3231
db.exec('CREATE TABLE t (x INTEGER)');
3332
calls = []; // reset after setup
@@ -37,14 +36,13 @@ suite('DatabaseSync trace option', () => {
3736

3837
assert.strictEqual(calls.length, 1);
3938
assert.strictEqual(calls[0], 'INSERT INTO t VALUES (42.0)');
40-
db.close();
4139
});
4240

43-
it('callback receives SQL string for SELECT statements', () => {
41+
it('callback receives SQL string for prepared SELECT statements', (t) => {
4442
let calls = [];
45-
const db = new DatabaseSync(':memory:', {
46-
trace: (sql) => calls.push(sql),
47-
});
43+
const db = new DatabaseSync(':memory:');
44+
t.after(() => db.close());
45+
db.setSqlTraceHook((sql) => calls.push(sql));
4846

4947
db.exec('CREATE TABLE t (x INTEGER)');
5048
db.exec('INSERT INTO t VALUES (1)');
@@ -55,14 +53,13 @@ suite('DatabaseSync trace option', () => {
5553

5654
assert.strictEqual(calls.length, 1);
5755
assert.strictEqual(calls[0], 'SELECT x FROM t WHERE x = 1.0');
58-
db.close();
5956
});
6057

61-
it('callback receives SQL string for UPDATE statements', () => {
58+
it('callback receives SQL string for prepared UPDATE statements', (t) => {
6259
let calls = [];
63-
const db = new DatabaseSync(':memory:', {
64-
trace: (sql) => calls.push(sql),
65-
});
60+
const db = new DatabaseSync(':memory:');
61+
t.after(() => db.close());
62+
db.setSqlTraceHook((sql) => calls.push(sql));
6663

6764
db.exec('CREATE TABLE t (x INTEGER)');
6865
db.exec('INSERT INTO t VALUES (1)');
@@ -73,14 +70,13 @@ suite('DatabaseSync trace option', () => {
7370

7471
assert.strictEqual(calls.length, 1);
7572
assert.strictEqual(calls[0], 'UPDATE t SET x = 2.0 WHERE x = 1.0');
76-
db.close();
7773
});
7874

79-
it('callback receives SQL string for DELETE statements', () => {
75+
it('callback receives SQL string for prepared DELETE statements', (t) => {
8076
let calls = [];
81-
const db = new DatabaseSync(':memory:', {
82-
trace: (sql) => calls.push(sql),
83-
});
77+
const db = new DatabaseSync(':memory:');
78+
t.after(() => db.close());
79+
db.setSqlTraceHook((sql) => calls.push(sql));
8480

8581
db.exec('CREATE TABLE t (x INTEGER)');
8682
db.exec('INSERT INTO t VALUES (1)');
@@ -91,16 +87,27 @@ suite('DatabaseSync trace option', () => {
9187

9288
assert.strictEqual(calls.length, 1);
9389
assert.strictEqual(calls[0], 'DELETE FROM t WHERE x = 1.0');
94-
db.close();
9590
});
9691

97-
it('falls back to source SQL when expansion fails', () => {
98-
let calls = [];
92+
it('setSqlTraceHook(null) clears the callback', (t) => {
93+
const calls = [];
94+
const db = new DatabaseSync(':memory:');
95+
t.after(() => db.close());
96+
db.setSqlTraceHook((sql) => calls.push(sql));
9997

100-
const db = new DatabaseSync(':memory:', {
101-
trace: (sql) => calls.push(sql),
102-
limits: { length: 1000 },
103-
});
98+
db.exec('CREATE TABLE t (x INTEGER)');
99+
assert.strictEqual(calls.length, 1);
100+
101+
db.setSqlTraceHook(null);
102+
db.exec('INSERT INTO t VALUES (1)');
103+
assert.strictEqual(calls.length, 1); // No new calls after clearing
104+
});
105+
106+
it('falls back to source SQL when expansion fails', (t) => {
107+
let calls = [];
108+
const db = new DatabaseSync(':memory:', { limits: { length: 1000 } });
109+
t.after(() => db.close());
110+
db.setSqlTraceHook((sql) => calls.push(sql));
104111

105112
db.exec('CREATE TABLE t (x TEXT)');
106113
calls = []; // reset after setup
@@ -113,22 +120,35 @@ suite('DatabaseSync trace option', () => {
113120
assert.strictEqual(calls.length, 1);
114121
// Falls back to source SQL with unexpanded '?' placeholder
115122
assert.strictEqual(calls[0], 'INSERT INTO t VALUES (?)');
116-
db.close();
117123
});
118124

119-
it('invalid type for trace throws ERR_INVALID_ARG_TYPE', () => {
125+
it('throws when the database is not open', () => {
126+
const db = new DatabaseSync(':memory:', { open: false });
127+
128+
assert.throws(() => {
129+
db.setSqlTraceHook(() => {});
130+
}, {
131+
code: 'ERR_INVALID_STATE',
132+
message: /database is not open/,
133+
});
134+
});
135+
136+
it('throws ERR_INVALID_ARG_TYPE for non-function argument', (t) => {
137+
const db = new DatabaseSync(':memory:');
138+
t.after(() => db.close());
139+
120140
assert.throws(() => {
121-
new DatabaseSync(':memory:', { trace: 'not-a-function' });
141+
db.setSqlTraceHook('not-a-function');
122142
}, {
123143
code: 'ERR_INVALID_ARG_TYPE',
124-
message: /The "options\.trace" argument must be a function\./,
144+
message: /The "hook" argument must be a function\./,
125145
});
126146

127147
assert.throws(() => {
128-
new DatabaseSync(':memory:', { trace: 42 });
148+
db.setSqlTraceHook(42);
129149
}, {
130150
code: 'ERR_INVALID_ARG_TYPE',
131-
message: /The "options\.trace" argument must be a function\./,
151+
message: /The "hook" argument must be a function\./,
132152
});
133153
});
134154
});

0 commit comments

Comments
 (0)