Skip to content

Commit 31a01e8

Browse files
feat: introduce setLogger method
1 parent 67c25d3 commit 31a01e8

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

lib/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ const addon = bindings<{
4848
databaseClose(db: NativeDatabase): void;
4949

5050
signalTokenize(value: string): Array<string>;
51+
52+
setLogger(fn: (code: string, message: string) => void): void;
5153
}>(ROOT_DIR);
5254

5355
export type RunResult = {
@@ -587,4 +589,12 @@ export default class Database {
587589
}
588590
}
589591

590-
export { Database };
592+
function setLogger(fn: (code: string, message: string) => void): void {
593+
if (typeof fn !== 'function') {
594+
throw new TypeError('Invalid value');
595+
}
596+
597+
return addon.setLogger(fn);
598+
}
599+
600+
export { Database, setLogger };

src/addon.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,48 @@ static Napi::Value SignalTokenize(const Napi::CallbackInfo& info) {
7474
return result;
7575
}
7676

77+
// Global Settings
78+
79+
thread_local Napi::Reference<Napi::Function> logger_fn_;
80+
81+
static void LoggerWrapper(void* _ctx, int code, const char* msg) {
82+
if (logger_fn_.IsEmpty()) {
83+
return;
84+
}
85+
86+
auto env = logger_fn_.Env();
87+
Napi::HandleScope scope(env);
88+
89+
#define CODE_STR(NAME) \
90+
case NAME: \
91+
code_name = #NAME; \
92+
break;
93+
94+
const char* code_name;
95+
switch (code) {
96+
SQLITE_ERROR_ENUM(CODE_STR)
97+
default:
98+
code_name = "unknown";
99+
break;
100+
}
101+
102+
#undef CODE_STR
103+
104+
logger_fn_.Value().Call({
105+
Napi::String::New(env, code_name),
106+
Napi::String::New(env, msg),
107+
});
108+
}
109+
110+
static void SetLogger(const Napi::CallbackInfo& info) {
111+
auto callback = info[0].As<Napi::Function>();
112+
assert(callback.IsFunction());
113+
114+
logger_fn_.Reset(callback, 1);
115+
116+
sqlite3_config(SQLITE_CONFIG_LOG, LoggerWrapper);
117+
}
118+
77119
// Utils
78120

79121
Napi::Error FormatError(Napi::Env env, const char* format, ...) {
@@ -856,6 +898,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
856898

857899
Database::Init(env, exports);
858900
Statement::Init(env, exports);
901+
exports["setLogger"] = Napi::Function::New(env, &SetLogger);
859902
exports["signalTokenize"] = Napi::Function::New(env, &SignalTokenize);
860903
return exports;
861904
}

test/memory.test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, test, beforeEach, afterEach } from 'vitest';
22

3-
import Database from '../lib/index.js';
3+
import Database, { setLogger } from '../lib/index.js';
44

55
const rows = [
66
{
@@ -180,6 +180,21 @@ test('persistent statement recompilation', () => {
180180
});
181181
});
182182

183+
test('setLogger should log on statement recompilation', () => {
184+
const messages = new Array<{ code: string; message: string }>();
185+
setLogger((code, message) => messages.push({ code, message }));
186+
187+
const stmt = db.prepare('SELECT * FROM t', { persistent: true });
188+
189+
db.exec(`ALTER TABLE t ADD COLUMN d TEXT DEFAULT 'hello'`);
190+
191+
expect(stmt.get()).not.toBeUndefined();
192+
193+
expect(messages).toHaveLength(1);
194+
expect(messages[0]?.code).toEqual('SQLITE_SCHEMA');
195+
expect(messages[0]?.message).toMatch(/database schema has changed/);
196+
});
197+
183198
describe('list parameters', () => {
184199
test('correct count', () => {
185200
expect(db.prepare('SELECT * FROM t WHERE a > ?').get([2])).toEqual(rows[2]);

0 commit comments

Comments
 (0)