@velarscript-labs/sqlite 0.2.2 → 0.3.2
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/CHANGELOG.md +25 -0
- package/README.md +44 -12
- package/dist/index.js +505 -81
- package/dist/index.js.map +1 -1
- package/dist/index.veli.json +1310 -678
- package/dist/velar-library.json +6 -6
- package/package.json +6 -5
- package/src/index.vel +182 -124
package/dist/velar-library.json
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
"abiVersion": 1,
|
|
5
5
|
"package": {
|
|
6
6
|
"name": "@velarscript-labs/sqlite",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.3.2"
|
|
8
8
|
},
|
|
9
9
|
"target": "node",
|
|
10
|
-
"compilerVersion": "0.
|
|
10
|
+
"compilerVersion": "0.15.0",
|
|
11
11
|
"sourceEntry": "src/index.vel",
|
|
12
12
|
"sources": [
|
|
13
13
|
{
|
|
14
14
|
"path": "src/index.vel",
|
|
15
|
-
"sha256": "
|
|
15
|
+
"sha256": "d35ec69e071fdeab0e379a78209c7f075cabf1b9b5148c9ba033a325e9c482b6"
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
18
|
"entry": {
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"sourceMap": "index.js.map",
|
|
21
21
|
"interface": "index.veli.json",
|
|
22
22
|
"sha256": {
|
|
23
|
-
"javascript": "
|
|
24
|
-
"sourceMap": "
|
|
25
|
-
"interface": "
|
|
23
|
+
"javascript": "33086d56ae3878ed41d4c31405ce57e17b8aca65ec3160a9a82ceada7b4b49d7",
|
|
24
|
+
"sourceMap": "d2e01a92404964ef4674985191acf85be17185a1ea75fb2369f5acaf6fb8aecf",
|
|
25
|
+
"interface": "93ed6516f8b9c501b068b4550424e472d536e1e7a9b3451bba536b3c3e736012"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velarscript-labs/sqlite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Injection-resistant bounded SQLite capability for VelarScript Node applications.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"test": "velar test"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=24"
|
|
20
|
+
"node": ">=24.12"
|
|
21
21
|
},
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
"entry": "src/index.vel",
|
|
35
35
|
"artifacts": { "node": "dist/velar-library.json" },
|
|
36
36
|
"targets": ["node"],
|
|
37
|
-
"requires": {"capabilities": ["node"], "language": ">=0.
|
|
37
|
+
"requires": {"capabilities": ["node"], "language": ">=0.15 <0.16"}
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@velarscript-labs/database": "0.
|
|
40
|
+
"@velarscript-labs/database": "0.3.2",
|
|
41
|
+
"sqlite3-parser": "0.7.1"
|
|
41
42
|
}
|
|
42
43
|
}
|
package/src/index.vel
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
/// Per-connection bounds. Every operation is serialized through one Worker;
|
|
2
2
|
/// queueCapacity bounds admitted operations waiting for that connection.
|
|
3
|
-
import {DatabaseExecutor} from "@velarscript-labs/database"
|
|
3
|
+
import {DatabaseExecutor, DatabaseStatement, trustedSql} from "@velarscript-labs/database"
|
|
4
|
+
|
|
5
|
+
export enum SqliteJournalMode:
|
|
6
|
+
delete
|
|
7
|
+
truncate
|
|
8
|
+
persist
|
|
9
|
+
memory
|
|
10
|
+
wal
|
|
11
|
+
off
|
|
4
12
|
|
|
5
13
|
export type SqliteOptions:
|
|
6
14
|
busyTimeoutMilliseconds: number?
|
|
7
15
|
queueCapacity: number?
|
|
8
16
|
maxRows: number?
|
|
9
17
|
maxResultBytes: number?
|
|
18
|
+
statementCacheCapacity: number?
|
|
19
|
+
journalMode: SqliteJournalMode?
|
|
20
|
+
readOnly: bool?
|
|
10
21
|
|
|
11
|
-
extern js
|
|
22
|
+
extern js`
|
|
12
23
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
13
24
|
import { Worker } from "node:worker_threads";
|
|
14
25
|
|
|
@@ -51,21 +62,31 @@ extern js()`
|
|
|
51
62
|
|
|
52
63
|
function checkedOptions(value) {
|
|
53
64
|
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError("SQLite options must be a record");
|
|
54
|
-
const allowed = new Set(["busyTimeoutMilliseconds", "queueCapacity", "maxRows", "maxResultBytes"]);
|
|
65
|
+
const allowed = new Set(["busyTimeoutMilliseconds", "queueCapacity", "maxRows", "maxResultBytes", "statementCacheCapacity", "journalMode", "readOnly"]);
|
|
55
66
|
for (const key of Object.keys(value)) if (!allowed.has(key)) throw new TypeError("Unknown SQLite option " + key);
|
|
67
|
+
const journalModes = new Set(["delete", "truncate", "persist", "memory", "wal", "off"]);
|
|
68
|
+
const journalMode = value.journalMode == null ? null : value.journalMode;
|
|
69
|
+
if (journalMode !== null && (typeof journalMode !== "string" || !journalModes.has(journalMode))) throw new TypeError("SQLite journalMode must name a supported mode");
|
|
70
|
+
if (value.readOnly != null && typeof value.readOnly !== "boolean") throw new TypeError("SQLite readOnly must be bool");
|
|
71
|
+
if (value.readOnly === true && journalMode !== null) throw new TypeError("SQLite readOnly connections cannot change journalMode");
|
|
56
72
|
return {
|
|
57
73
|
busyTimeoutMilliseconds: integer(value.busyTimeoutMilliseconds, 2000, 1, 60000, "SQLite busy timeout"),
|
|
58
74
|
queueCapacity: integer(value.queueCapacity, 64, 1, 1024, "SQLite queue capacity"),
|
|
59
75
|
maxRows: integer(value.maxRows, 10000, 1, 1000000, "SQLite row limit"),
|
|
60
76
|
maxResultBytes: integer(value.maxResultBytes, 64 * 1024 * 1024, 1, 128 * 1024 * 1024, "SQLite result byte limit"),
|
|
77
|
+
statementCacheCapacity: integer(value.statementCacheCapacity, 128, 1, 1024, "SQLite statement cache capacity"),
|
|
78
|
+
journalMode,
|
|
79
|
+
readOnly: value.readOnly === true,
|
|
61
80
|
};
|
|
62
81
|
}
|
|
63
82
|
|
|
64
83
|
function workerMain() {
|
|
65
84
|
const { Buffer } = require("node:buffer");
|
|
66
85
|
const { parentPort, workerData } = require("node:worker_threads");
|
|
67
|
-
const { DatabaseSync } = require("node:sqlite");
|
|
86
|
+
const { DatabaseSync, constants } = require("node:sqlite");
|
|
87
|
+
const { parseStmt } = require("sqlite3-parser");
|
|
68
88
|
let database = null;
|
|
89
|
+
let statements = null;
|
|
69
90
|
let closed = false;
|
|
70
91
|
|
|
71
92
|
function failure(message, code, operation, retryable = false) {
|
|
@@ -89,13 +110,6 @@ extern js()`
|
|
|
89
110
|
};
|
|
90
111
|
}
|
|
91
112
|
|
|
92
|
-
function sqlText(value, operation) {
|
|
93
|
-
if (typeof value !== "string" || value.length === 0 || value.length > 1024 * 1024 || value.includes("\0")) {
|
|
94
|
-
throw failure("SQLite SQL must be non-empty text no longer than 1 MiB", "SQLITE_INPUT", operation);
|
|
95
|
-
}
|
|
96
|
-
return value;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
113
|
function parameter(value, budget) {
|
|
100
114
|
if (value === null) return null;
|
|
101
115
|
if (typeof value === "boolean") return value ? 1 : 0;
|
|
@@ -124,6 +138,29 @@ extern js()`
|
|
|
124
138
|
return output;
|
|
125
139
|
}
|
|
126
140
|
|
|
141
|
+
function statementParts(rawFragments, rawParameters, operation) {
|
|
142
|
+
if (!Array.isArray(rawFragments) || rawFragments.length !== rawParameters.length + 1 || rawFragments.length > 1000) {
|
|
143
|
+
throw failure("SQLite statements require exactly one more SQL fragment than bound parameters", "SQLITE_INPUT", operation);
|
|
144
|
+
}
|
|
145
|
+
let sourceBytes = 0;
|
|
146
|
+
const fragments = rawFragments.map(fragment => {
|
|
147
|
+
if (typeof fragment !== "string" || fragment.includes("\0") || fragment.includes("?")) {
|
|
148
|
+
throw failure("SQLite SQL fragments must be text without NUL or raw parameter placeholders", "SQLITE_INPUT", operation);
|
|
149
|
+
}
|
|
150
|
+
sourceBytes += Buffer.byteLength(fragment, "utf8");
|
|
151
|
+
return fragment;
|
|
152
|
+
});
|
|
153
|
+
if (sourceBytes === 0 || sourceBytes > 1024 * 1024) throw failure("SQLite SQL must be non-empty text no longer than 1 MiB", "SQLITE_INPUT", operation);
|
|
154
|
+
const bound = parameters(rawParameters);
|
|
155
|
+
const source = fragments.join("?");
|
|
156
|
+
const parsed = parseStmt(source);
|
|
157
|
+
if (parsed.status !== "ok") {
|
|
158
|
+
const message = parsed.errors.length === 0 ? "invalid statement" : parsed.errors[0].message;
|
|
159
|
+
throw failure("SQLite requires exactly one valid statement: " + message, "SQLITE_INPUT", operation);
|
|
160
|
+
}
|
|
161
|
+
return { fragments, bound };
|
|
162
|
+
}
|
|
163
|
+
|
|
127
164
|
function count(value, label) {
|
|
128
165
|
const number = typeof value === "bigint" ? Number(value) : value;
|
|
129
166
|
if (!Number.isSafeInteger(number) || number < 0) throw failure(label + " is outside VelarScript's safe integer range", "SQLITE_INTEGER_RANGE", "result");
|
|
@@ -165,30 +202,24 @@ extern js()`
|
|
|
165
202
|
return output;
|
|
166
203
|
}
|
|
167
204
|
|
|
168
|
-
function statement(sql, operation) {
|
|
169
|
-
const prepared = database.prepare(sqlText(sql, operation));
|
|
170
|
-
if (typeof prepared.setReadBigInts === "function") prepared.setReadBigInts(true);
|
|
171
|
-
return prepared;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
205
|
function execute(message) {
|
|
175
|
-
const
|
|
176
|
-
const result =
|
|
206
|
+
const statement = statementParts(message.fragments, message.params, "execute");
|
|
207
|
+
const result = statements.run(statement.fragments, ...statement.bound);
|
|
177
208
|
return count(result.changes, "SQLite affected row count");
|
|
178
209
|
}
|
|
179
210
|
|
|
180
211
|
function one(message) {
|
|
181
|
-
const
|
|
182
|
-
const row =
|
|
212
|
+
const statement = statementParts(message.fragments, message.params, "one");
|
|
213
|
+
const row = statements.get(statement.fragments, ...statement.bound);
|
|
183
214
|
if (row === undefined) return null;
|
|
184
215
|
return resultRow(row, { value: 0 });
|
|
185
216
|
}
|
|
186
217
|
|
|
187
218
|
function all(message) {
|
|
188
|
-
const
|
|
219
|
+
const statement = statementParts(message.fragments, message.params, "all");
|
|
189
220
|
const rows = [];
|
|
190
221
|
const budget = { value: 0 };
|
|
191
|
-
for (const row of
|
|
222
|
+
for (const row of statements.iterate(statement.fragments, ...statement.bound)) {
|
|
192
223
|
if (rows.length >= workerData.maxRows) throw failure("SQLite result exceeds the connection row limit", "SQLITE_ROW_LIMIT", "all");
|
|
193
224
|
rows.push(resultRow(row, budget));
|
|
194
225
|
}
|
|
@@ -227,8 +258,39 @@ extern js()`
|
|
|
227
258
|
}
|
|
228
259
|
|
|
229
260
|
try {
|
|
230
|
-
database = new DatabaseSync(workerData.path, {
|
|
231
|
-
|
|
261
|
+
database = new DatabaseSync(workerData.path, {
|
|
262
|
+
timeout: workerData.busyTimeoutMilliseconds,
|
|
263
|
+
readOnly: workerData.readOnly,
|
|
264
|
+
enableForeignKeyConstraints: true,
|
|
265
|
+
enableDoubleQuotedStringLiterals: false,
|
|
266
|
+
allowExtension: false,
|
|
267
|
+
readBigInts: true,
|
|
268
|
+
allowBareNamedParameters: false,
|
|
269
|
+
allowUnknownNamedParameters: false,
|
|
270
|
+
defensive: true,
|
|
271
|
+
limits: {
|
|
272
|
+
length: workerData.maxResultBytes,
|
|
273
|
+
sqlLength: 1024 * 1024,
|
|
274
|
+
column: 2000,
|
|
275
|
+
exprDepth: 1000,
|
|
276
|
+
compoundSelect: 500,
|
|
277
|
+
vdbeOp: 250000,
|
|
278
|
+
functionArg: 1000,
|
|
279
|
+
attach: 0,
|
|
280
|
+
likePatternLength: 50000,
|
|
281
|
+
variableNumber: 999,
|
|
282
|
+
triggerDepth: 100,
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
database.enableDefensive(true);
|
|
286
|
+
database.enableLoadExtension(false);
|
|
287
|
+
database.setAuthorizer((actionCode, arg1, arg2) => {
|
|
288
|
+
if (actionCode === constants.SQLITE_ATTACH || actionCode === constants.SQLITE_DETACH) return constants.SQLITE_DENY;
|
|
289
|
+
if (actionCode === constants.SQLITE_FUNCTION && typeof arg2 === "string" && arg2.toLowerCase() === "load_extension") return constants.SQLITE_DENY;
|
|
290
|
+
return constants.SQLITE_OK;
|
|
291
|
+
});
|
|
292
|
+
statements = database.createTagStore(workerData.statementCacheCapacity);
|
|
293
|
+
if (workerData.journalMode !== null) database.exec("PRAGMA journal_mode = " + workerData.journalMode.toUpperCase());
|
|
232
294
|
parentPort.postMessage({ kind: "ready" });
|
|
233
295
|
} catch (error) {
|
|
234
296
|
parentPort.postMessage({ kind: "ready", error: errorData(error, "open") });
|
|
@@ -261,13 +323,13 @@ extern js()`
|
|
|
261
323
|
state.pending.clear();
|
|
262
324
|
}
|
|
263
325
|
|
|
264
|
-
function request(state, operation,
|
|
326
|
+
function request(state, operation, fragments = [""], params = []) {
|
|
265
327
|
if (state.closed) return Promise.reject(new NativeSqliteError("SQLite connection is closed", "SQLITE_CLOSED", operation));
|
|
266
328
|
const id = state.nextId++;
|
|
267
329
|
return new Promise((resolve, reject) => {
|
|
268
330
|
state.pending.set(id, { resolve, reject });
|
|
269
331
|
try {
|
|
270
|
-
state.worker.postMessage({ id, operation,
|
|
332
|
+
state.worker.postMessage({ id, operation, fragments, params });
|
|
271
333
|
} catch (error) {
|
|
272
334
|
state.pending.delete(id);
|
|
273
335
|
reject(error);
|
|
@@ -287,11 +349,11 @@ extern js()`
|
|
|
287
349
|
return state;
|
|
288
350
|
}
|
|
289
351
|
|
|
290
|
-
function transactionRequest(transaction, operation,
|
|
352
|
+
function transactionRequest(transaction, operation, fragments = [""], params = []) {
|
|
291
353
|
const state = transactionStateOf(transaction);
|
|
292
354
|
if (state.busy) return Promise.reject(new NativeSqliteConcurrencyError("SQLite transaction operations must be awaited one at a time"));
|
|
293
355
|
state.busy = true;
|
|
294
|
-
return request(state.connection, operation,
|
|
356
|
+
return request(state.connection, operation, fragments, params).finally(() => { state.busy = false; });
|
|
295
357
|
}
|
|
296
358
|
|
|
297
359
|
function enqueue(state, operation) {
|
|
@@ -310,9 +372,9 @@ extern js()`
|
|
|
310
372
|
transactionStates.set(this, state);
|
|
311
373
|
}
|
|
312
374
|
|
|
313
|
-
execute(
|
|
314
|
-
one(
|
|
315
|
-
all(
|
|
375
|
+
execute(fragments, params = []) { return transactionRequest(this, "execute", fragments, params); }
|
|
376
|
+
one(fragments, params = []) { return transactionRequest(this, "one", fragments, params); }
|
|
377
|
+
all(fragments, params = []) { return transactionRequest(this, "all", fragments, params); }
|
|
316
378
|
}
|
|
317
379
|
|
|
318
380
|
export class NativeSqliteConnection {
|
|
@@ -321,19 +383,19 @@ extern js()`
|
|
|
321
383
|
connectionStates.set(this, state);
|
|
322
384
|
}
|
|
323
385
|
|
|
324
|
-
execute(
|
|
386
|
+
execute(fragments, params = []) {
|
|
325
387
|
const state = stateOf(this);
|
|
326
|
-
return enqueue(state, () => request(state, "execute",
|
|
388
|
+
return enqueue(state, () => request(state, "execute", fragments, params));
|
|
327
389
|
}
|
|
328
390
|
|
|
329
|
-
one(
|
|
391
|
+
one(fragments, params = []) {
|
|
330
392
|
const state = stateOf(this);
|
|
331
|
-
return enqueue(state, () => request(state, "one",
|
|
393
|
+
return enqueue(state, () => request(state, "one", fragments, params));
|
|
332
394
|
}
|
|
333
395
|
|
|
334
|
-
all(
|
|
396
|
+
all(fragments, params = []) {
|
|
335
397
|
const state = stateOf(this);
|
|
336
|
-
return enqueue(state, () => request(state, "all",
|
|
398
|
+
return enqueue(state, () => request(state, "all", fragments, params));
|
|
337
399
|
}
|
|
338
400
|
|
|
339
401
|
transaction(operation) {
|
|
@@ -448,14 +510,14 @@ extern js()`
|
|
|
448
510
|
const message: string
|
|
449
511
|
|
|
450
512
|
export class NativeSqliteTransaction:
|
|
451
|
-
async def execute(
|
|
452
|
-
async def one(
|
|
453
|
-
async def all(
|
|
513
|
+
async def execute(fragments: readonly List<string>, params: readonly List<unknown> = []) -> number
|
|
514
|
+
async def one(fragments: readonly List<string>, params: readonly List<unknown> = []) -> unknown
|
|
515
|
+
async def all(fragments: readonly List<string>, params: readonly List<unknown> = []) -> List<unknown>
|
|
454
516
|
|
|
455
517
|
export class NativeSqliteConnection:
|
|
456
|
-
async def execute(
|
|
457
|
-
async def one(
|
|
458
|
-
async def all(
|
|
518
|
+
async def execute(fragments: readonly List<string>, params: readonly List<unknown> = []) -> number
|
|
519
|
+
async def one(fragments: readonly List<string>, params: readonly List<unknown> = []) -> unknown
|
|
520
|
+
async def all(fragments: readonly List<string>, params: readonly List<unknown> = []) -> List<unknown>
|
|
459
521
|
async def transaction(operation: (NativeSqliteTransaction) -> Promise<null>) -> null
|
|
460
522
|
async def close() -> null
|
|
461
523
|
|
|
@@ -475,160 +537,156 @@ export class SqliteError extends Error:
|
|
|
475
537
|
self.retryable = retryable
|
|
476
538
|
|
|
477
539
|
export class SqliteBackpressureError extends SqliteError:
|
|
478
|
-
constructor(message: string = "SQLite queue is full"):
|
|
479
|
-
super(message, "SQLITE_BACKPRESSURE", "queue", true)
|
|
540
|
+
constructor(message: string = "SQLite queue is full"): super(message, "SQLITE_BACKPRESSURE", "queue", true)
|
|
480
541
|
|
|
481
542
|
export class SqliteConcurrencyError extends SqliteError:
|
|
482
543
|
constructor(message: string = "SQLite connection cannot be used from its transaction callback"):
|
|
483
544
|
super(message, "SQLITE_CONCURRENCY", "transaction", true)
|
|
484
545
|
|
|
485
546
|
def sqliteFailure(error: NativeSqliteError) -> SqliteError:
|
|
486
|
-
if error is NativeSqliteBackpressureError:
|
|
487
|
-
|
|
488
|
-
if error is
|
|
489
|
-
return SqliteConcurrencyError(error.message)
|
|
490
|
-
if error is NativeSqliteError:
|
|
491
|
-
return SqliteError(error.message, error.sqliteCode, error.operation, error.retryable)
|
|
547
|
+
if error is NativeSqliteBackpressureError: return SqliteBackpressureError(error.message)
|
|
548
|
+
if error is NativeSqliteConcurrencyError: return SqliteConcurrencyError(error.message)
|
|
549
|
+
if error is NativeSqliteError: return SqliteError(error.message, error.sqliteCode, error.operation, error.retryable)
|
|
492
550
|
return SqliteError(error.message)
|
|
493
551
|
|
|
552
|
+
/// Produces a SQLite literal only for grammar positions where bound parameters
|
|
553
|
+
/// are not legal, such as schema defaults and PRAGMA assignments. Ordinary
|
|
554
|
+
/// query values must still use sqlParameter/sqlTuple/sqlRows.
|
|
555
|
+
export def sqliteLiteral(value: unknown) -> DatabaseStatement:
|
|
556
|
+
if value == null: return trustedSql("NULL")
|
|
557
|
+
if value is bool: return trustedSql(value ? "1" : "0")
|
|
558
|
+
if value is string: return trustedSql("'" + value.replaceAll("'", "''") + "'")
|
|
559
|
+
if value is number:
|
|
560
|
+
assert value.isFinite() else "SQLite numeric literals must be finite"
|
|
561
|
+
assert not value.isInteger() or (value >= -9007199254740991 and value <= 9007199254740991) else "SQLite integer literals must be safe integers"
|
|
562
|
+
return trustedSql(str(value))
|
|
563
|
+
throw Error("SQLite literals support only null, bool, string, and finite number")
|
|
564
|
+
|
|
565
|
+
/// Quotes one SQLite identifier as a single name. Qualified names must compose
|
|
566
|
+
/// separately quoted identifiers around trusted punctuation.
|
|
567
|
+
export def sqliteIdentifier(value: string) -> DatabaseStatement:
|
|
568
|
+
assert not value.isBlank() and value.size <= 255 and not value.has(Text.fromCodePoint(0)) else "SQLite identifiers must be non-blank text with no NUL and no longer than 255 characters"
|
|
569
|
+
return trustedSql(`"` + value.replaceAll(`"`, `""`) + `"`)
|
|
570
|
+
|
|
494
571
|
export class SqliteTransaction:
|
|
495
|
-
constructor(private const native: NativeSqliteTransaction):
|
|
496
|
-
pass
|
|
572
|
+
constructor(private const native: NativeSqliteTransaction): pass
|
|
497
573
|
|
|
498
|
-
async def execute(
|
|
499
|
-
|
|
500
|
-
|
|
574
|
+
async def execute(statement: DatabaseStatement) -> number:
|
|
575
|
+
const data = statement.data()
|
|
576
|
+
try: return await self.native.execute(data.fragments, data.parameters)
|
|
501
577
|
catch error:
|
|
502
|
-
if error is NativeSqliteError:
|
|
503
|
-
throw sqliteFailure(error)
|
|
578
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
504
579
|
throw SqliteError(error.message)
|
|
505
580
|
|
|
506
581
|
/// Projects this transaction into the engine-neutral functional operation layer.
|
|
507
582
|
def executor() -> DatabaseExecutor:
|
|
508
|
-
async def executeStatement(
|
|
509
|
-
return await self.execute(text, parameters)
|
|
583
|
+
async def executeStatement(statement: DatabaseStatement) -> number: return await self.execute(statement)
|
|
510
584
|
|
|
511
|
-
async def oneRow(
|
|
512
|
-
|
|
513
|
-
|
|
585
|
+
async def oneRow(statement: DatabaseStatement) -> unknown:
|
|
586
|
+
const data = statement.data()
|
|
587
|
+
try: return await self.native.one(data.fragments, data.parameters)
|
|
514
588
|
catch error:
|
|
515
|
-
if error is NativeSqliteError:
|
|
516
|
-
throw sqliteFailure(error)
|
|
589
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
517
590
|
throw SqliteError(error.message)
|
|
518
591
|
|
|
519
|
-
async def allRows(
|
|
520
|
-
|
|
521
|
-
|
|
592
|
+
async def allRows(statement: DatabaseStatement) -> List<unknown>:
|
|
593
|
+
const data = statement.data()
|
|
594
|
+
try: return await self.native.all(data.fragments, data.parameters)
|
|
522
595
|
catch error:
|
|
523
|
-
if error is NativeSqliteError:
|
|
524
|
-
throw sqliteFailure(error)
|
|
596
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
525
597
|
throw SqliteError(error.message)
|
|
526
598
|
|
|
527
599
|
return {execute: executeStatement, one: oneRow, all: allRows}
|
|
528
600
|
|
|
529
|
-
async def one<T>(
|
|
601
|
+
async def one<T>(statement: DatabaseStatement, RowType: Type<T>) -> T?:
|
|
602
|
+
const data = statement.data()
|
|
530
603
|
try:
|
|
531
|
-
const row = await self.native.one(
|
|
532
|
-
if row == null:
|
|
533
|
-
return null
|
|
604
|
+
const row = await self.native.one(data.fragments, data.parameters)
|
|
605
|
+
if row == null: return null
|
|
534
606
|
return RowType.parse(row)
|
|
535
607
|
catch error:
|
|
536
|
-
if error is NativeSqliteError:
|
|
537
|
-
throw sqliteFailure(error)
|
|
608
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
538
609
|
throw SqliteError(error.message)
|
|
539
610
|
|
|
540
|
-
async def all<T>(
|
|
611
|
+
async def all<T>(statement: DatabaseStatement, RowType: Type<T>) -> List<T>:
|
|
612
|
+
const data = statement.data()
|
|
541
613
|
try:
|
|
542
|
-
const rows = await self.native.all(
|
|
614
|
+
const rows = await self.native.all(data.fragments, data.parameters)
|
|
543
615
|
return rows.map(row => RowType.parse(row))
|
|
544
616
|
catch error:
|
|
545
|
-
if error is NativeSqliteError:
|
|
546
|
-
throw sqliteFailure(error)
|
|
617
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
547
618
|
throw SqliteError(error.message)
|
|
548
619
|
|
|
549
620
|
export class SqliteConnection:
|
|
550
|
-
constructor(private const native: NativeSqliteConnection):
|
|
551
|
-
pass
|
|
621
|
+
constructor(private const native: NativeSqliteConnection): pass
|
|
552
622
|
|
|
553
|
-
async def execute(
|
|
554
|
-
|
|
555
|
-
|
|
623
|
+
async def execute(statement: DatabaseStatement) -> number:
|
|
624
|
+
const data = statement.data()
|
|
625
|
+
try: return await self.native.execute(data.fragments, data.parameters)
|
|
556
626
|
catch error:
|
|
557
|
-
if error is NativeSqliteError:
|
|
558
|
-
throw sqliteFailure(error)
|
|
627
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
559
628
|
throw SqliteError(error.message)
|
|
560
629
|
|
|
561
630
|
/// Projects this connection into the engine-neutral functional operation layer.
|
|
562
631
|
def executor() -> DatabaseExecutor:
|
|
563
|
-
async def executeStatement(
|
|
564
|
-
return await self.execute(text, parameters)
|
|
632
|
+
async def executeStatement(statement: DatabaseStatement) -> number: return await self.execute(statement)
|
|
565
633
|
|
|
566
|
-
async def oneRow(
|
|
567
|
-
|
|
568
|
-
|
|
634
|
+
async def oneRow(statement: DatabaseStatement) -> unknown:
|
|
635
|
+
const data = statement.data()
|
|
636
|
+
try: return await self.native.one(data.fragments, data.parameters)
|
|
569
637
|
catch error:
|
|
570
|
-
if error is NativeSqliteError:
|
|
571
|
-
throw sqliteFailure(error)
|
|
638
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
572
639
|
throw SqliteError(error.message)
|
|
573
640
|
|
|
574
|
-
async def allRows(
|
|
575
|
-
|
|
576
|
-
|
|
641
|
+
async def allRows(statement: DatabaseStatement) -> List<unknown>:
|
|
642
|
+
const data = statement.data()
|
|
643
|
+
try: return await self.native.all(data.fragments, data.parameters)
|
|
577
644
|
catch error:
|
|
578
|
-
if error is NativeSqliteError:
|
|
579
|
-
throw sqliteFailure(error)
|
|
645
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
580
646
|
throw SqliteError(error.message)
|
|
581
647
|
|
|
582
648
|
return {execute: executeStatement, one: oneRow, all: allRows}
|
|
583
649
|
|
|
584
|
-
async def one<T>(
|
|
650
|
+
async def one<T>(statement: DatabaseStatement, RowType: Type<T>) -> T?:
|
|
651
|
+
const data = statement.data()
|
|
585
652
|
try:
|
|
586
|
-
const row = await self.native.one(
|
|
587
|
-
if row == null:
|
|
588
|
-
return null
|
|
653
|
+
const row = await self.native.one(data.fragments, data.parameters)
|
|
654
|
+
if row == null: return null
|
|
589
655
|
return RowType.parse(row)
|
|
590
656
|
catch error:
|
|
591
|
-
if error is NativeSqliteError:
|
|
592
|
-
throw sqliteFailure(error)
|
|
657
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
593
658
|
throw SqliteError(error.message)
|
|
594
659
|
|
|
595
|
-
async def all<T>(
|
|
660
|
+
async def all<T>(statement: DatabaseStatement, RowType: Type<T>) -> List<T>:
|
|
661
|
+
const data = statement.data()
|
|
596
662
|
try:
|
|
597
|
-
const rows = await self.native.all(
|
|
663
|
+
const rows = await self.native.all(data.fragments, data.parameters)
|
|
598
664
|
return rows.map(row => RowType.parse(row))
|
|
599
665
|
catch error:
|
|
600
|
-
if error is NativeSqliteError:
|
|
601
|
-
throw sqliteFailure(error)
|
|
666
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
602
667
|
throw SqliteError(error.message)
|
|
603
668
|
|
|
604
669
|
async def transaction<T>(operation: (SqliteTransaction) -> Promise<T>) -> T:
|
|
605
670
|
const results: List<T> = []
|
|
606
|
-
async def run<T>(native: NativeSqliteTransaction):
|
|
607
|
-
results.append(await operation(SqliteTransaction(native)))
|
|
671
|
+
async def run<T>(native: NativeSqliteTransaction): results.append(await operation(SqliteTransaction(native)))
|
|
608
672
|
try:
|
|
609
673
|
await self.native.transaction(run)
|
|
610
674
|
assert results.size == 1 else "SQLite transaction did not return exactly one result"
|
|
611
675
|
return results[0]
|
|
612
676
|
catch error:
|
|
613
|
-
if error is NativeSqliteError:
|
|
614
|
-
throw sqliteFailure(error)
|
|
677
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
615
678
|
throw error
|
|
616
679
|
|
|
617
680
|
async def close():
|
|
618
|
-
try:
|
|
619
|
-
await self.native.close()
|
|
681
|
+
try: await self.native.close()
|
|
620
682
|
catch error:
|
|
621
|
-
if error is NativeSqliteError:
|
|
622
|
-
throw sqliteFailure(error)
|
|
683
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
623
684
|
throw SqliteError(error.message)
|
|
624
685
|
|
|
625
|
-
@dispose:
|
|
626
|
-
await self.close()
|
|
686
|
+
@dispose: await self.close()
|
|
627
687
|
|
|
628
688
|
export async def openSqlite(path: string, options: SqliteOptions = {}) -> SqliteConnection:
|
|
629
|
-
try:
|
|
630
|
-
return SqliteConnection(await openNativeSqlite(path, options))
|
|
689
|
+
try: return SqliteConnection(await openNativeSqlite(path, options))
|
|
631
690
|
catch error:
|
|
632
|
-
if error is NativeSqliteError:
|
|
633
|
-
throw sqliteFailure(error)
|
|
691
|
+
if error is NativeSqliteError: throw sqliteFailure(error)
|
|
634
692
|
throw SqliteError(error.message)
|