@velarscript-labs/sqlite 0.2.1 → 0.3.1

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.
@@ -0,0 +1,28 @@
1
+ {
2
+ "formatVersion": 1,
3
+ "kind": "velar-library-artifact",
4
+ "abiVersion": 1,
5
+ "package": {
6
+ "name": "@velarscript-labs/sqlite",
7
+ "version": "0.3.1"
8
+ },
9
+ "target": "node",
10
+ "compilerVersion": "0.14.2",
11
+ "sourceEntry": "src/index.vel",
12
+ "sources": [
13
+ {
14
+ "path": "src/index.vel",
15
+ "sha256": "2a561a3d17d372c8f0e95e416bc1e0b829b45e807b3454292c2191beb24a5d18"
16
+ }
17
+ ],
18
+ "entry": {
19
+ "javascript": "index.js",
20
+ "sourceMap": "index.js.map",
21
+ "interface": "index.veli.json",
22
+ "sha256": {
23
+ "javascript": "bf5a8456e0778533840efb831a60b93ee23b3f0963735fccd29a89978a61358d",
24
+ "sourceMap": "6b8fe8ab61326511de62813d3e33a16aacf9f53ec46b16e20eb1cf68e05e2391",
25
+ "interface": "c340ec9d62adc30409be6de430791696e15cddb9e2b89da8b5d55cf444fc87b2"
26
+ }
27
+ }
28
+ }
package/package.json CHANGED
@@ -1,21 +1,23 @@
1
1
  {
2
2
  "name": "@velarscript-labs/sqlite",
3
- "version": "0.2.1",
4
- "description": "Bounded asynchronous SQLite capability for VelarScript Node applications.",
3
+ "version": "0.3.1",
4
+ "description": "Injection-resistant bounded SQLite capability for VelarScript Node applications.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "files": [
8
8
  "src/index.vel",
9
+ "dist",
9
10
  "README.md",
10
11
  "CHANGELOG.md",
11
12
  "LICENSE"
12
13
  ],
13
14
  "scripts": {
15
+ "build": "velar build-library",
14
16
  "check": "velar format --check && velar check",
15
17
  "test": "velar test"
16
18
  },
17
19
  "engines": {
18
- "node": ">=24"
20
+ "node": ">=24.12"
19
21
  },
20
22
  "repository": {
21
23
  "type": "git",
@@ -25,12 +27,17 @@
25
27
  "publishConfig": {
26
28
  "access": "public"
27
29
  },
30
+ "exports": {
31
+ ".": "./dist/index.js"
32
+ },
28
33
  "velar": {
29
34
  "entry": "src/index.vel",
35
+ "artifacts": { "node": "dist/velar-library.json" },
30
36
  "targets": ["node"],
31
- "requires": {"capabilities": ["node"], "language": ">=0.13 <0.15"}
37
+ "requires": {"capabilities": ["node"], "language": ">=0.14 <0.15"}
32
38
  },
33
39
  "dependencies": {
34
- "@velarscript-labs/database": "0.2.1"
40
+ "@velarscript-labs/database": "0.3.1",
41
+ "sqlite3-parser": "0.7.1"
35
42
  }
36
43
  }
package/src/index.vel CHANGED
@@ -1,12 +1,23 @@
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
22
  extern js()`
12
23
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -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 prepared = statement(message.sql, "execute");
176
- const result = prepared.run(...parameters(message.params));
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 prepared = statement(message.sql, "one");
182
- const row = prepared.get(...parameters(message.params));
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 prepared = statement(message.sql, "all");
219
+ const statement = statementParts(message.fragments, message.params, "all");
189
220
  const rows = [];
190
221
  const budget = { value: 0 };
191
- for (const row of prepared.iterate(...parameters(message.params))) {
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, { timeout: workerData.busyTimeoutMilliseconds });
231
- database.exec("PRAGMA foreign_keys = ON");
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, sql = "", params = []) {
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, sql, params });
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, sql = "", params = []) {
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, sql, params).finally(() => { state.busy = false; });
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(sql, params = []) { return transactionRequest(this, "execute", sql, params); }
314
- one(sql, params = []) { return transactionRequest(this, "one", sql, params); }
315
- all(sql, params = []) { return transactionRequest(this, "all", sql, params); }
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(sql, params = []) {
386
+ execute(fragments, params = []) {
325
387
  const state = stateOf(this);
326
- return enqueue(state, () => request(state, "execute", sql, params));
388
+ return enqueue(state, () => request(state, "execute", fragments, params));
327
389
  }
328
390
 
329
- one(sql, params = []) {
391
+ one(fragments, params = []) {
330
392
  const state = stateOf(this);
331
- return enqueue(state, () => request(state, "one", sql, params));
393
+ return enqueue(state, () => request(state, "one", fragments, params));
332
394
  }
333
395
 
334
- all(sql, params = []) {
396
+ all(fragments, params = []) {
335
397
  const state = stateOf(this);
336
- return enqueue(state, () => request(state, "all", sql, params));
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(sql: string, params: List<unknown> = []) -> number
452
- async def one(sql: string, params: List<unknown> = []) -> unknown
453
- async def all(sql: string, params: List<unknown> = []) -> List<unknown>
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(sql: string, params: List<unknown> = []) -> number
457
- async def one(sql: string, params: List<unknown> = []) -> unknown
458
- async def all(sql: string, params: List<unknown> = []) -> List<unknown>
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
 
@@ -491,13 +553,36 @@ def sqliteFailure(error: NativeSqliteError) -> SqliteError:
491
553
  return SqliteError(error.message, error.sqliteCode, error.operation, error.retryable)
492
554
  return SqliteError(error.message)
493
555
 
556
+ /// Produces a SQLite literal only for grammar positions where bound parameters
557
+ /// are not legal, such as schema defaults and PRAGMA assignments. Ordinary
558
+ /// query values must still use sqlParameter/sqlTuple/sqlRows.
559
+ export def sqliteLiteral(value: unknown) -> DatabaseStatement:
560
+ if value == null:
561
+ return trustedSql("NULL")
562
+ if value is bool:
563
+ return trustedSql(value ? "1" : "0")
564
+ if value is string:
565
+ return trustedSql("'" + value.replaceAll("'", "''") + "'")
566
+ if value is number:
567
+ assert value.isFinite() else "SQLite numeric literals must be finite"
568
+ assert not value.isInteger() or (value >= -9007199254740991 and value <= 9007199254740991) else "SQLite integer literals must be safe integers"
569
+ return trustedSql(str(value))
570
+ throw Error("SQLite literals support only null, bool, string, and finite number")
571
+
572
+ /// Quotes one SQLite identifier as a single name. Qualified names must compose
573
+ /// separately quoted identifiers around trusted punctuation.
574
+ export def sqliteIdentifier(value: string) -> DatabaseStatement:
575
+ 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"
576
+ return trustedSql(`"` + value.replaceAll(`"`, `""`) + `"`)
577
+
494
578
  export class SqliteTransaction:
495
579
  constructor(private const native: NativeSqliteTransaction):
496
580
  pass
497
581
 
498
- async def execute(sql: string, params: List<unknown> = []) -> number:
582
+ async def execute(statement: DatabaseStatement) -> number:
583
+ const data = statement.data()
499
584
  try:
500
- return await self.native.execute(sql, params)
585
+ return await self.native.execute(data.fragments, data.parameters)
501
586
  catch error:
502
587
  if error is NativeSqliteError:
503
588
  throw sqliteFailure(error)
@@ -505,20 +590,22 @@ export class SqliteTransaction:
505
590
 
506
591
  /// Projects this transaction into the engine-neutral functional operation layer.
507
592
  def executor() -> DatabaseExecutor:
508
- async def executeStatement(text: string, parameters: List<unknown>) -> number:
509
- return await self.execute(text, parameters)
593
+ async def executeStatement(statement: DatabaseStatement) -> number:
594
+ return await self.execute(statement)
510
595
 
511
- async def oneRow(text: string, parameters: List<unknown>) -> unknown:
596
+ async def oneRow(statement: DatabaseStatement) -> unknown:
597
+ const data = statement.data()
512
598
  try:
513
- return await self.native.one(text, parameters)
599
+ return await self.native.one(data.fragments, data.parameters)
514
600
  catch error:
515
601
  if error is NativeSqliteError:
516
602
  throw sqliteFailure(error)
517
603
  throw SqliteError(error.message)
518
604
 
519
- async def allRows(text: string, parameters: List<unknown>) -> List<unknown>:
605
+ async def allRows(statement: DatabaseStatement) -> List<unknown>:
606
+ const data = statement.data()
520
607
  try:
521
- return await self.native.all(text, parameters)
608
+ return await self.native.all(data.fragments, data.parameters)
522
609
  catch error:
523
610
  if error is NativeSqliteError:
524
611
  throw sqliteFailure(error)
@@ -526,9 +613,10 @@ export class SqliteTransaction:
526
613
 
527
614
  return {execute: executeStatement, one: oneRow, all: allRows}
528
615
 
529
- async def one<T>(sql: string, RowType: Type<T>, params: List<unknown> = []) -> T?:
616
+ async def one<T>(statement: DatabaseStatement, RowType: Type<T>) -> T?:
617
+ const data = statement.data()
530
618
  try:
531
- const row = await self.native.one(sql, params)
619
+ const row = await self.native.one(data.fragments, data.parameters)
532
620
  if row == null:
533
621
  return null
534
622
  return RowType.parse(row)
@@ -537,9 +625,10 @@ export class SqliteTransaction:
537
625
  throw sqliteFailure(error)
538
626
  throw SqliteError(error.message)
539
627
 
540
- async def all<T>(sql: string, RowType: Type<T>, params: List<unknown> = []) -> List<T>:
628
+ async def all<T>(statement: DatabaseStatement, RowType: Type<T>) -> List<T>:
629
+ const data = statement.data()
541
630
  try:
542
- const rows = await self.native.all(sql, params)
631
+ const rows = await self.native.all(data.fragments, data.parameters)
543
632
  return rows.map(row => RowType.parse(row))
544
633
  catch error:
545
634
  if error is NativeSqliteError:
@@ -550,9 +639,10 @@ export class SqliteConnection:
550
639
  constructor(private const native: NativeSqliteConnection):
551
640
  pass
552
641
 
553
- async def execute(sql: string, params: List<unknown> = []) -> number:
642
+ async def execute(statement: DatabaseStatement) -> number:
643
+ const data = statement.data()
554
644
  try:
555
- return await self.native.execute(sql, params)
645
+ return await self.native.execute(data.fragments, data.parameters)
556
646
  catch error:
557
647
  if error is NativeSqliteError:
558
648
  throw sqliteFailure(error)
@@ -560,20 +650,22 @@ export class SqliteConnection:
560
650
 
561
651
  /// Projects this connection into the engine-neutral functional operation layer.
562
652
  def executor() -> DatabaseExecutor:
563
- async def executeStatement(text: string, parameters: List<unknown>) -> number:
564
- return await self.execute(text, parameters)
653
+ async def executeStatement(statement: DatabaseStatement) -> number:
654
+ return await self.execute(statement)
565
655
 
566
- async def oneRow(text: string, parameters: List<unknown>) -> unknown:
656
+ async def oneRow(statement: DatabaseStatement) -> unknown:
657
+ const data = statement.data()
567
658
  try:
568
- return await self.native.one(text, parameters)
659
+ return await self.native.one(data.fragments, data.parameters)
569
660
  catch error:
570
661
  if error is NativeSqliteError:
571
662
  throw sqliteFailure(error)
572
663
  throw SqliteError(error.message)
573
664
 
574
- async def allRows(text: string, parameters: List<unknown>) -> List<unknown>:
665
+ async def allRows(statement: DatabaseStatement) -> List<unknown>:
666
+ const data = statement.data()
575
667
  try:
576
- return await self.native.all(text, parameters)
668
+ return await self.native.all(data.fragments, data.parameters)
577
669
  catch error:
578
670
  if error is NativeSqliteError:
579
671
  throw sqliteFailure(error)
@@ -581,9 +673,10 @@ export class SqliteConnection:
581
673
 
582
674
  return {execute: executeStatement, one: oneRow, all: allRows}
583
675
 
584
- async def one<T>(sql: string, RowType: Type<T>, params: List<unknown> = []) -> T?:
676
+ async def one<T>(statement: DatabaseStatement, RowType: Type<T>) -> T?:
677
+ const data = statement.data()
585
678
  try:
586
- const row = await self.native.one(sql, params)
679
+ const row = await self.native.one(data.fragments, data.parameters)
587
680
  if row == null:
588
681
  return null
589
682
  return RowType.parse(row)
@@ -592,9 +685,10 @@ export class SqliteConnection:
592
685
  throw sqliteFailure(error)
593
686
  throw SqliteError(error.message)
594
687
 
595
- async def all<T>(sql: string, RowType: Type<T>, params: List<unknown> = []) -> List<T>:
688
+ async def all<T>(statement: DatabaseStatement, RowType: Type<T>) -> List<T>:
689
+ const data = statement.data()
596
690
  try:
597
- const rows = await self.native.all(sql, params)
691
+ const rows = await self.native.all(data.fragments, data.parameters)
598
692
  return rows.map(row => RowType.parse(row))
599
693
  catch error:
600
694
  if error is NativeSqliteError: