Skip to content

Commit 3c36722

Browse files
committed
sqlite: rename to setSqlTraceHook
1 parent 27985a1 commit 3c36722

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
@@ -130,9 +130,6 @@ exposed by this class execute synchronously.
130130
<!-- YAML
131131
added: v22.5.0
132132
changes:
133-
- version: REPLACEME
134-
pr-url: https://github.com/nodejs/node/pull/62241
135-
description: Add `trace` option.
136133
- version:
137134
- v25.5.0
138135
- v24.14.0
@@ -206,13 +203,6 @@ changes:
206203
* `likePatternLength` {number} Maximum length of a LIKE pattern.
207204
* `variableNumber` {number} Maximum number of SQL variables.
208205
* `triggerDepth` {number} Maximum trigger recursion depth.
209-
* `trace` {Function} An optional callback function that is invoked for
210-
every SQL statement executed against the database. The callback receives
211-
the expanded SQL string (with bound parameter values substituted) as its
212-
only argument. If expansion fails, the source SQL (with unsubstituted
213-
placeholders) is passed instead. This is useful for logging and debugging.
214-
This option is a wrapper around [`sqlite3_trace_v2()`][].
215-
**Default:** `undefined`.
216206

217207
Constructs a new `DatabaseSync` instance.
218208

@@ -405,6 +395,56 @@ added:
405395
This method is used to create SQLite user-defined functions. This method is a
406396
wrapper around [`sqlite3_create_function_v2()`][].
407397

398+
### `database.setSqlTraceHook(hook)`
399+
400+
<!-- YAML
401+
added: REPLACEME
402+
-->
403+
404+
* `hook` {Function|null} The trace function to set, or `null` to clear
405+
the current hook.
406+
407+
Sets a hook that SQLite invokes for every SQL statement executed against the
408+
database. The hook receives the expanded SQL string (with bound parameter
409+
values substituted) as its only argument. If expansion fails, the source SQL
410+
with unsubstituted placeholders is passed instead.
411+
412+
This method is a wrapper around [`sqlite3_trace_v2()`][].
413+
414+
```cjs
415+
const { DatabaseSync } = require('node:sqlite');
416+
const db = new DatabaseSync(':memory:');
417+
418+
db.setSqlTraceHook((sql) => console.log(sql));
419+
420+
db.exec('CREATE TABLE t (x INTEGER)');
421+
// Logs: CREATE TABLE t (x INTEGER)
422+
423+
const stmt = db.prepare('INSERT INTO t VALUES (?)');
424+
stmt.run(42);
425+
// Logs: INSERT INTO t VALUES (42.0)
426+
427+
// Clear the hook
428+
db.setSqlTraceHook(null);
429+
```
430+
431+
```mjs
432+
import { DatabaseSync } from 'node:sqlite';
433+
const db = new DatabaseSync(':memory:');
434+
435+
db.setSqlTraceHook((sql) => console.log(sql));
436+
437+
db.exec('CREATE TABLE t (x INTEGER)');
438+
// Logs: CREATE TABLE t (x INTEGER)
439+
440+
const stmt = db.prepare('INSERT INTO t VALUES (?)');
441+
stmt.run(42);
442+
// Logs: INSERT INTO t VALUES (42.0)
443+
444+
// Clear the hook
445+
db.setSqlTraceHook(null);
446+
```
447+
408448
### `database.setAuthorizer(callback)`
409449

410450
<!-- 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(
@@ -2310,6 +2286,30 @@ void DatabaseSync::LoadExtension(const FunctionCallbackInfo<Value>& args) {
23102286
}
23112287
}
23122288

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