@homeofthings/sqlite3 6.1.1 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/binding.gyp +48 -9
- package/lib/sqlite3.d.ts +163 -14
- package/lib/sqlite3.js +6 -0
- package/package.json +11 -8
- package/src/database.cc +15 -2
- package/src/macros.h +21 -3
- package/src/node_sqlite3.cc +11 -0
package/README.md
CHANGED
|
@@ -36,9 +36,9 @@ yarn add @homeofthings/sqlite3
|
|
|
36
36
|
|
|
37
37
|
### Prebuilt binaries
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
`@homeofthings/sqlite3` uses [Node-API](https://nodejs.org/api/n-api.html) so prebuilt binaries do not need to be built for specific Node versions. Prebuilt binaries are available for Node-API v3 and v6. Check the [Node-API version matrix](https://nodejs.org/api/n-api.html#node-api-version-matrix) to ensure your Node version supports one of these. Requires Node.js v20.17.0 or later.
|
|
40
40
|
|
|
41
|
-
The module uses [`prebuild-install`](https://github.com/prebuild/prebuild-install) to download the prebuilt binary for your platform, if it exists. These binaries are hosted on GitHub Releases
|
|
41
|
+
The module uses [`prebuild-install`](https://github.com/prebuild/prebuild-install) to download the prebuilt binary for your platform, if it exists. These binaries are hosted on GitHub Releases. The following targets are currently provided:
|
|
42
42
|
|
|
43
43
|
* `darwin-arm64`
|
|
44
44
|
* `darwin-x64`
|
|
@@ -64,7 +64,46 @@ SQLite's [SQLCipher extension](https://github.com/sqlcipher/sqlcipher) is also
|
|
|
64
64
|
|
|
65
65
|
# API
|
|
66
66
|
|
|
67
|
-
See the [API documentation](
|
|
67
|
+
See the [API documentation](docs/API.md) for detailed documentation of both the callback-based and Promise-based APIs.
|
|
68
|
+
|
|
69
|
+
## Quick Example
|
|
70
|
+
|
|
71
|
+
### Callback-based API (Traditional)
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
const sqlite3 = require('@homeofthings/sqlite3').verbose();
|
|
75
|
+
const db = new sqlite3.Database(':memory:');
|
|
76
|
+
|
|
77
|
+
db.serialize(() => {
|
|
78
|
+
db.run("CREATE TABLE lorem (info TEXT)");
|
|
79
|
+
db.run("INSERT INTO lorem VALUES (?)", ['test']);
|
|
80
|
+
db.each("SELECT * FROM lorem", (err, row) => {
|
|
81
|
+
console.log(row);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
db.close();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Promise-based API (Modern)
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
const { SqliteDatabase } = require('@homeofthings/sqlite3');
|
|
92
|
+
|
|
93
|
+
async function main() {
|
|
94
|
+
const db = await SqliteDatabase.open(':memory:');
|
|
95
|
+
|
|
96
|
+
await db.run("CREATE TABLE lorem (info TEXT)");
|
|
97
|
+
await db.run("INSERT INTO lorem VALUES (?)", ['test']);
|
|
98
|
+
|
|
99
|
+
const rows = await db.all("SELECT * FROM lorem");
|
|
100
|
+
console.log(rows);
|
|
101
|
+
|
|
102
|
+
await db.close();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main().catch(console.error);
|
|
106
|
+
```
|
|
68
107
|
|
|
69
108
|
# Usage
|
|
70
109
|
|
package/binding.gyp
CHANGED
|
@@ -8,14 +8,10 @@
|
|
|
8
8
|
"targets": [
|
|
9
9
|
{
|
|
10
10
|
"target_name": "<(module_name)",
|
|
11
|
-
"
|
|
12
|
-
"cflags_cc!": [ "-fno-exceptions" ],
|
|
13
|
-
"xcode_settings": { "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
|
11
|
+
"xcode_settings": {
|
|
14
12
|
"CLANG_CXX_LIBRARY": "libc++",
|
|
15
13
|
"MACOSX_DEPLOYMENT_TARGET": "10.7",
|
|
16
|
-
|
|
17
|
-
"msvs_settings": {
|
|
18
|
-
"VCCLCompilerTool": { "ExceptionHandling": 1 },
|
|
14
|
+
"OTHER_CFLAGS": [ "-fstack-protector-strong" ]
|
|
19
15
|
},
|
|
20
16
|
"include_dirs": [
|
|
21
17
|
"<!@(node -p \"require('node-addon-api').include\")"],
|
|
@@ -40,11 +36,32 @@
|
|
|
40
36
|
},
|
|
41
37
|
{
|
|
42
38
|
"dependencies": [
|
|
43
|
-
"<!(node -p \"require('node-addon-api').
|
|
39
|
+
"<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except",
|
|
44
40
|
"deps/sqlite3.gyp:sqlite3"
|
|
45
41
|
]
|
|
46
42
|
}
|
|
47
|
-
]
|
|
43
|
+
],
|
|
44
|
+
# Linux hardening flags (apply to all builds)
|
|
45
|
+
["OS=='linux'", {
|
|
46
|
+
"cflags+": [
|
|
47
|
+
"-fstack-protector-strong",
|
|
48
|
+
"-fPIC"
|
|
49
|
+
],
|
|
50
|
+
"ldflags+": [ "-Wl,-z,relro,-z,now" ]
|
|
51
|
+
}],
|
|
52
|
+
# Windows hardening flags (apply to all builds)
|
|
53
|
+
["OS=='win'", {
|
|
54
|
+
"msvs_settings": {
|
|
55
|
+
"VCCLCompilerTool": {
|
|
56
|
+
"ExceptionHandling": 1,
|
|
57
|
+
"BufferSecurityCheck": "true",
|
|
58
|
+
"ControlFlowGuard": "Guard"
|
|
59
|
+
},
|
|
60
|
+
"VCLinkerTool": {
|
|
61
|
+
"AdditionalOptions": [ "/DYNAMICBASE", "/NXCOMPAT" ]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}]
|
|
48
65
|
],
|
|
49
66
|
"sources": [
|
|
50
67
|
"src/backup.cc",
|
|
@@ -52,7 +69,29 @@
|
|
|
52
69
|
"src/node_sqlite3.cc",
|
|
53
70
|
"src/statement.cc"
|
|
54
71
|
],
|
|
55
|
-
"defines": [ "NAPI_VERSION=<(napi_build_version)"
|
|
72
|
+
"defines": [ "NAPI_VERSION=<(napi_build_version)" ],
|
|
73
|
+
# Release-specific hardening flags
|
|
74
|
+
"configurations": {
|
|
75
|
+
"Release": {
|
|
76
|
+
"conditions": [
|
|
77
|
+
# _FORTIFY_SOURCE applies to all Linux architectures
|
|
78
|
+
["OS=='linux'", {
|
|
79
|
+
"defines+": [ "_FORTIFY_SOURCE=2" ]
|
|
80
|
+
}],
|
|
81
|
+
# Control Flow Protection only for x86_64 (Intel CET)
|
|
82
|
+
["OS=='linux' and target_arch=='x64'", {
|
|
83
|
+
"cflags+": [ "-fcf-protection=full" ]
|
|
84
|
+
}],
|
|
85
|
+
["OS=='win'", {
|
|
86
|
+
"msvs_settings": {
|
|
87
|
+
"VCCLCompilerTool": {
|
|
88
|
+
"AdditionalOptions": [ "/sdl" ]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}]
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
56
95
|
}
|
|
57
96
|
]
|
|
58
97
|
}
|
package/lib/sqlite3.d.ts
CHANGED
|
@@ -26,23 +26,23 @@ export const BUSY: number;
|
|
|
26
26
|
export const LOCKED: number;
|
|
27
27
|
export const NOMEM: number;
|
|
28
28
|
export const READONLY: number;
|
|
29
|
-
export const INTERRUPT: number
|
|
29
|
+
export const INTERRUPT: number;
|
|
30
30
|
export const IOERR: number;
|
|
31
|
-
export const CORRUPT: number
|
|
31
|
+
export const CORRUPT: number;
|
|
32
32
|
export const NOTFOUND: number;
|
|
33
33
|
export const FULL: number;
|
|
34
34
|
export const CANTOPEN: number;
|
|
35
35
|
export const PROTOCOL: number;
|
|
36
36
|
export const EMPTY: number;
|
|
37
37
|
export const SCHEMA: number;
|
|
38
|
-
export const TOOBIG: number
|
|
39
|
-
export const CONSTRAINT: number
|
|
38
|
+
export const TOOBIG: number;
|
|
39
|
+
export const CONSTRAINT: number;
|
|
40
40
|
export const MISMATCH: number;
|
|
41
41
|
export const MISUSE: number;
|
|
42
42
|
export const NOLFS: number;
|
|
43
|
-
export const AUTH: number
|
|
43
|
+
export const AUTH: number;
|
|
44
44
|
export const FORMAT: number;
|
|
45
|
-
export const RANGE: number
|
|
45
|
+
export const RANGE: number;
|
|
46
46
|
export const NOTADB: number;
|
|
47
47
|
|
|
48
48
|
export const LIMIT_LENGTH: number;
|
|
@@ -64,8 +64,8 @@ export const cached: {
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
export interface RunResult extends Statement {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
lastID: number;
|
|
68
|
+
changes: number;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export class Statement extends events.EventEmitter {
|
|
@@ -143,6 +143,17 @@ export class Database extends events.EventEmitter {
|
|
|
143
143
|
|
|
144
144
|
export function verbose(): sqlite3;
|
|
145
145
|
|
|
146
|
+
export interface Backup {
|
|
147
|
+
idle: boolean;
|
|
148
|
+
completed: boolean;
|
|
149
|
+
failed: boolean;
|
|
150
|
+
remaining: number;
|
|
151
|
+
pageCount: number;
|
|
152
|
+
step(pages: number, callback: (err: Error | null) => void): void;
|
|
153
|
+
finish(): void;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
146
157
|
export interface sqlite3 {
|
|
147
158
|
OPEN_READONLY: number;
|
|
148
159
|
OPEN_READWRITE: number;
|
|
@@ -165,23 +176,23 @@ export interface sqlite3 {
|
|
|
165
176
|
LOCKED: number;
|
|
166
177
|
NOMEM: number;
|
|
167
178
|
READONLY: number;
|
|
168
|
-
INTERRUPT: number
|
|
179
|
+
INTERRUPT: number;
|
|
169
180
|
IOERR: number;
|
|
170
|
-
CORRUPT: number
|
|
181
|
+
CORRUPT: number;
|
|
171
182
|
NOTFOUND: number;
|
|
172
183
|
FULL: number;
|
|
173
184
|
CANTOPEN: number;
|
|
174
185
|
PROTOCOL: number;
|
|
175
186
|
EMPTY: number;
|
|
176
187
|
SCHEMA: number;
|
|
177
|
-
TOOBIG: number
|
|
178
|
-
CONSTRAINT: number
|
|
188
|
+
TOOBIG: number;
|
|
189
|
+
CONSTRAINT: number;
|
|
179
190
|
MISMATCH: number;
|
|
180
191
|
MISUSE: number;
|
|
181
192
|
NOLFS: number;
|
|
182
|
-
AUTH: number
|
|
193
|
+
AUTH: number;
|
|
183
194
|
FORMAT: number;
|
|
184
|
-
RANGE: number
|
|
195
|
+
RANGE: number;
|
|
185
196
|
NOTADB: number;
|
|
186
197
|
|
|
187
198
|
LIMIT_LENGTH: number;
|
|
@@ -201,5 +212,143 @@ export interface sqlite3 {
|
|
|
201
212
|
RunResult: RunResult;
|
|
202
213
|
Statement: typeof Statement;
|
|
203
214
|
Database: typeof Database;
|
|
215
|
+
Backup: Backup;
|
|
216
|
+
SqlRunResult: SqlRunResult;
|
|
217
|
+
SqliteDatabase: typeof SqliteDatabase;
|
|
218
|
+
SqliteStatement: typeof SqliteStatement;
|
|
219
|
+
SqliteBackup: typeof SqliteBackup;
|
|
204
220
|
verbose(): this;
|
|
205
221
|
}
|
|
222
|
+
|
|
223
|
+
// Promise-based wrapper classes
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Result object returned by run() operations
|
|
227
|
+
*/
|
|
228
|
+
export interface SqlRunResult {
|
|
229
|
+
lastID: number;
|
|
230
|
+
changes: number;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Promise-based wrapper for the Database class
|
|
235
|
+
*/
|
|
236
|
+
export class SqliteDatabase {
|
|
237
|
+
protected db?: Database;
|
|
238
|
+
|
|
239
|
+
constructor();
|
|
240
|
+
|
|
241
|
+
static open(filename: string, mode?: number): Promise<SqliteDatabase>;
|
|
242
|
+
|
|
243
|
+
open(filename: string, mode?: number): Promise<void>;
|
|
244
|
+
close(): Promise<void>;
|
|
245
|
+
|
|
246
|
+
run(sql: string, params?: any): Promise<SqlRunResult>;
|
|
247
|
+
get<T = any>(sql: string, params?: any): Promise<T | undefined>;
|
|
248
|
+
all<T = any>(sql: string, params?: any): Promise<T[]>;
|
|
249
|
+
each<T = any>(
|
|
250
|
+
sql: string,
|
|
251
|
+
params?: any,
|
|
252
|
+
callback?: (err: Error | null, row: T) => void,
|
|
253
|
+
): Promise<number>;
|
|
254
|
+
|
|
255
|
+
exec(sql: string): Promise<void>;
|
|
256
|
+
prepare(sql: string, params?: any): Promise<SqliteStatement>;
|
|
257
|
+
|
|
258
|
+
backup(
|
|
259
|
+
filename: string,
|
|
260
|
+
filenameIsDest?: boolean,
|
|
261
|
+
destName?: string,
|
|
262
|
+
sourceName?: string,
|
|
263
|
+
): Promise<SqliteBackup>;
|
|
264
|
+
|
|
265
|
+
serialize(callback?: () => void): void;
|
|
266
|
+
parallelize(callback?: () => void): void;
|
|
267
|
+
|
|
268
|
+
transactionalize<T>(callback: () => Promise<T>): Promise<T>;
|
|
269
|
+
beginTransaction(): Promise<void>;
|
|
270
|
+
commitTransaction(): Promise<void>;
|
|
271
|
+
rollbackTransaction(): Promise<void>;
|
|
272
|
+
endTransaction(commit: boolean): Promise<void>;
|
|
273
|
+
|
|
274
|
+
loadExtension(filename: string): Promise<void>;
|
|
275
|
+
|
|
276
|
+
wait(): Promise<void>;
|
|
277
|
+
interrupt(): void;
|
|
278
|
+
|
|
279
|
+
configure(option: string, ...args: any[]): void;
|
|
280
|
+
|
|
281
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
282
|
+
off(event: string, listener: (...args: any[]) => void): this;
|
|
283
|
+
removeAllListeners(event?: string): this;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Promise-based wrapper for the Statement class
|
|
288
|
+
*/
|
|
289
|
+
export class SqliteStatement {
|
|
290
|
+
constructor(stmt: Statement);
|
|
291
|
+
|
|
292
|
+
bind(...params: any[]): this;
|
|
293
|
+
reset(): Promise<void>;
|
|
294
|
+
|
|
295
|
+
run(params?: any): Promise<SqlRunResult>;
|
|
296
|
+
get<T = any>(params?: any): Promise<T | undefined>;
|
|
297
|
+
all<T = any>(params?: any): Promise<T[]>;
|
|
298
|
+
each<T = any>(
|
|
299
|
+
params?: any,
|
|
300
|
+
callback?: (err: Error | null, row: T) => void,
|
|
301
|
+
): Promise<number>;
|
|
302
|
+
|
|
303
|
+
finalize(): Promise<void>;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Promise-based wrapper for the Backup class
|
|
308
|
+
*/
|
|
309
|
+
export class SqliteBackup {
|
|
310
|
+
constructor(backup: Backup);
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Returns true if the backup is idle (not actively copying)
|
|
314
|
+
*/
|
|
315
|
+
readonly idle: boolean;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Returns true if the backup is completed
|
|
319
|
+
*/
|
|
320
|
+
readonly completed: boolean;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Returns true if the backup has failed
|
|
324
|
+
*/
|
|
325
|
+
readonly failed: boolean;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Returns the remaining number of pages left to copy
|
|
329
|
+
* Returns -1 if `step` not yet called
|
|
330
|
+
*/
|
|
331
|
+
readonly remaining: number;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Returns the total number of pages
|
|
335
|
+
* Returns -1 if `step` not yet called
|
|
336
|
+
*/
|
|
337
|
+
readonly pageCount: number;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Returns the progress (percentage completion)
|
|
341
|
+
*/
|
|
342
|
+
readonly progress: number;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Copy the next page or all remaining pages of the backup
|
|
346
|
+
* @param pages - Number of pages to copy (-1 for all remaining)
|
|
347
|
+
*/
|
|
348
|
+
step(pages?: number): Promise<void>;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Finish the backup (synchronous)
|
|
352
|
+
*/
|
|
353
|
+
finish(): void;
|
|
354
|
+
}
|
package/lib/sqlite3.js
CHANGED
|
@@ -205,3 +205,9 @@ sqlite3.verbose = function() {
|
|
|
205
205
|
|
|
206
206
|
return sqlite3;
|
|
207
207
|
};
|
|
208
|
+
|
|
209
|
+
// Export Promise-based wrapper classes
|
|
210
|
+
const { SqliteDatabase, SqliteStatement, SqliteBackup } = require('./promise');
|
|
211
|
+
sqlite3.SqliteDatabase = SqliteDatabase;
|
|
212
|
+
sqlite3.SqliteStatement = SqliteStatement;
|
|
213
|
+
sqlite3.SqliteBackup = SqliteBackup;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homeofthings/sqlite3",
|
|
3
3
|
"description": "Asynchronous, non-blocking SQLite3 bindings",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.3.0",
|
|
5
5
|
"homepage": "https://github.com/gms1/node-sqlite3",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Mapbox",
|
|
@@ -45,15 +45,18 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"bindings": "^1.5.0",
|
|
48
|
-
"node-addon-api": "^8.
|
|
48
|
+
"node-addon-api": "^8.7.0",
|
|
49
49
|
"prebuild-install": "^7.1.3",
|
|
50
|
-
"tar": "^7.5.
|
|
50
|
+
"tar": "^7.5.13"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"eslint": "
|
|
54
|
-
"
|
|
53
|
+
"@eslint/js": "^10.0.1",
|
|
54
|
+
"eslint": "^10.2.0",
|
|
55
|
+
"globals": "^17.4.0",
|
|
56
|
+
"mocha": "11.7.5",
|
|
57
|
+
"nyc": "^18.0.0",
|
|
55
58
|
"prebuild": "13.0.1",
|
|
56
|
-
"tinybench": "^
|
|
59
|
+
"tinybench": "^6.0.0"
|
|
57
60
|
},
|
|
58
61
|
"peerDependencies": {
|
|
59
62
|
"node-gyp": "12.x"
|
|
@@ -75,8 +78,8 @@
|
|
|
75
78
|
"rebuild": "node-gyp rebuild",
|
|
76
79
|
"upload": "prebuild --verbose --prerelease",
|
|
77
80
|
"frozen-install": "yarn install --frozen-lockfile",
|
|
78
|
-
"lint": "eslint
|
|
79
|
-
"test": "node test/support/createdb.js && mocha -R spec --timeout 480000"
|
|
81
|
+
"lint": "eslint lib/ test/ tools/",
|
|
82
|
+
"test": "node test/support/createdb.js && nyc mocha -R spec --timeout 480000"
|
|
80
83
|
},
|
|
81
84
|
"license": "BSD-3-Clause",
|
|
82
85
|
"publishConfig": {
|
package/src/database.cc
CHANGED
|
@@ -14,7 +14,7 @@ Napi::FunctionReference Database::constructor;
|
|
|
14
14
|
Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
|
|
15
15
|
Napi::HandleScope scope(env);
|
|
16
16
|
// declare napi_default_method here as it is only available in Node v14.12.0+
|
|
17
|
-
auto napi_default_method = static_cast<napi_property_attributes>(napi_writable | napi_configurable);
|
|
17
|
+
auto napi_default_method = static_cast<napi_property_attributes>(napi_writable | napi_configurable);
|
|
18
18
|
|
|
19
19
|
auto t = DefineClass(env, "Database", {
|
|
20
20
|
InstanceMethod("close", &Database::Close, napi_default_method),
|
|
@@ -81,8 +81,21 @@ void Database::Process() {
|
|
|
81
81
|
queue.pop();
|
|
82
82
|
std::unique_ptr<Call> call(c);
|
|
83
83
|
locked = call->exclusive;
|
|
84
|
+
|
|
85
|
+
// Track pending before callback to detect synchronous operations
|
|
86
|
+
unsigned int before_pending = pending;
|
|
84
87
|
call->callback(call->baton);
|
|
85
88
|
|
|
89
|
+
// If operation was synchronous (pending unchanged) and we're in exclusive mode,
|
|
90
|
+
// reset locked and continue processing the queue.
|
|
91
|
+
// Synchronous operations don't increment pending, so if pending is unchanged
|
|
92
|
+
// and locked is true, the operation completed synchronously.
|
|
93
|
+
if (locked && pending == before_pending) {
|
|
94
|
+
locked = false;
|
|
95
|
+
// Continue processing - don't break
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
86
99
|
if (locked) break;
|
|
87
100
|
}
|
|
88
101
|
}
|
|
@@ -340,7 +353,7 @@ Napi::Value Database::Configure(const Napi::CallbackInfo& info) {
|
|
|
340
353
|
REQUIRE_ARGUMENTS(2);
|
|
341
354
|
|
|
342
355
|
Napi::Function handle;
|
|
343
|
-
if (info[0].StrictEquals( Napi::String::New(env, "trace"))) {
|
|
356
|
+
if (info[0].StrictEquals( Napi::String::New(env, "trace"))) {
|
|
344
357
|
auto* baton = new Baton(db, handle);
|
|
345
358
|
db->Schedule(RegisterTraceCallback, baton);
|
|
346
359
|
}
|
package/src/macros.h
CHANGED
|
@@ -4,9 +4,14 @@
|
|
|
4
4
|
const char* sqlite_code_string(int code);
|
|
5
5
|
const char* sqlite_authorizer_string(int type);
|
|
6
6
|
#include <vector>
|
|
7
|
+
#include <atomic>
|
|
7
8
|
|
|
8
9
|
// TODO: better way to work around StringConcat?
|
|
9
10
|
#include <napi.h>
|
|
11
|
+
|
|
12
|
+
// Forward declaration of shutdown flag from node_sqlite3.cc
|
|
13
|
+
extern std::atomic<bool> g_env_shutting_down;
|
|
14
|
+
|
|
10
15
|
inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) {
|
|
11
16
|
return Napi::String::New(str1.Env(), str1.As<Napi::String>().Utf8Value() +
|
|
12
17
|
str2.As<Napi::String>().Utf8Value() );
|
|
@@ -128,8 +133,21 @@ inline bool OtherIsInt(Napi::Number source) {
|
|
|
128
133
|
if ((argc != 0) && (passed_argv != NULL)) {\
|
|
129
134
|
args.assign(passed_argv, passed_argv + argc);\
|
|
130
135
|
}\
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
try {\
|
|
137
|
+
Napi::Value res = (callback).Call(Napi::Value(context), args);\
|
|
138
|
+
if (res.IsEmpty()) return __VA_ARGS__;\
|
|
139
|
+
} catch (const Napi::Error&) {\
|
|
140
|
+
/* During Node.js/Electron shutdown, when using NAPI_CPP_EXCEPTIONS,*/\
|
|
141
|
+
/* we must take care to not throw any exceptions. */\
|
|
142
|
+
/* But when napi_call_function returns napi_cannot_run_js */\
|
|
143
|
+
/* this throws a C++ exception, when NAPI_CPP_EXCEPTIONS is enabled. */\
|
|
144
|
+
if (g_env_shutting_down.load()) {\
|
|
145
|
+
/* We need to silently ignore exceptions during shutdown. */\
|
|
146
|
+
return __VA_ARGS__;\
|
|
147
|
+
}\
|
|
148
|
+
/* Real rror - re-throw to propagate it */\
|
|
149
|
+
throw;\
|
|
150
|
+
}
|
|
133
151
|
|
|
134
152
|
#define WORK_DEFINITION(name) \
|
|
135
153
|
Napi::Value name(const Napi::CallbackInfo& info); \
|
|
@@ -204,4 +222,4 @@ inline bool OtherIsInt(Napi::Number source) {
|
|
|
204
222
|
backup->Process(); \
|
|
205
223
|
backup->db->Process();
|
|
206
224
|
|
|
207
|
-
#endif
|
|
225
|
+
#endif
|
package/src/node_sqlite3.cc
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include <sstream>
|
|
3
3
|
#include <cstring>
|
|
4
4
|
#include <string>
|
|
5
|
+
#include <atomic>
|
|
5
6
|
#include <sqlite3.h>
|
|
6
7
|
|
|
7
8
|
#include "macros.h"
|
|
@@ -11,11 +12,21 @@
|
|
|
11
12
|
|
|
12
13
|
using namespace node_sqlite3;
|
|
13
14
|
|
|
15
|
+
// Global flag set when the environment is shutting down.
|
|
16
|
+
std::atomic<bool> g_env_shutting_down{false};
|
|
17
|
+
|
|
18
|
+
static void EnvCleanupHook(void* /*data*/) {
|
|
19
|
+
g_env_shutting_down.store(true);
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
namespace {
|
|
15
23
|
|
|
16
24
|
Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) {
|
|
17
25
|
Napi::HandleScope scope(env);
|
|
18
26
|
|
|
27
|
+
// Register cleanup hook to detect shutdown
|
|
28
|
+
napi_add_env_cleanup_hook(env, EnvCleanupHook, nullptr);
|
|
29
|
+
|
|
19
30
|
Database::Init(env, exports);
|
|
20
31
|
Statement::Init(env, exports);
|
|
21
32
|
Backup::Init(env, exports);
|