@peerbit/indexer-sqlite3 3.0.6 → 3.0.7
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/dist/assets/sqlite3/sqlite3.worker.min.js +6 -1
- package/dist/benchmark/query-planner.d.ts +2 -0
- package/dist/benchmark/query-planner.d.ts.map +1 -0
- package/dist/benchmark/query-planner.js +97 -0
- package/dist/benchmark/query-planner.js.map +1 -0
- package/dist/index.min.js +535 -183
- package/dist/index.min.js.map +3 -3
- package/dist/src/engine.d.ts +15 -4
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +364 -179
- package/dist/src/engine.js.map +1 -1
- package/dist/src/query-planner.d.ts +20 -0
- package/dist/src/query-planner.d.ts.map +1 -1
- package/dist/src/query-planner.js +191 -21
- package/dist/src/query-planner.js.map +1 -1
- package/dist/src/schema.d.ts +6 -2
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +11 -8
- package/dist/src/schema.js.map +1 -1
- package/dist/src/sqlite3-messages.worker.d.ts +8 -1
- package/dist/src/sqlite3-messages.worker.d.ts.map +1 -1
- package/dist/src/sqlite3-messages.worker.js.map +1 -1
- package/dist/src/sqlite3.browser.d.ts.map +1 -1
- package/dist/src/sqlite3.browser.js +21 -0
- package/dist/src/sqlite3.browser.js.map +1 -1
- package/dist/src/sqlite3.wasm.d.ts.map +1 -1
- package/dist/src/sqlite3.wasm.js +4 -1
- package/dist/src/sqlite3.wasm.js.map +1 -1
- package/dist/src/sqlite3.worker.js +6 -0
- package/dist/src/sqlite3.worker.js.map +1 -1
- package/dist/src/types.d.ts +4 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/engine.ts +464 -235
- package/src/query-planner.ts +247 -22
- package/src/schema.ts +21 -4
- package/src/sqlite3-messages.worker.ts +6 -0
- package/src/sqlite3.browser.ts +33 -0
- package/src/sqlite3.wasm.ts +4 -1
- package/src/sqlite3.worker.ts +6 -0
- package/src/types.ts +3 -0
package/dist/src/engine.js
CHANGED
|
@@ -56,7 +56,9 @@ async function getIgnoreFK(stmt, values) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
const createBatchInsertSQL = (table, rows) => {
|
|
59
|
-
const columns = table.fields
|
|
59
|
+
const columns = table.fields
|
|
60
|
+
.map((field) => escapeColumnName(field.name))
|
|
61
|
+
.join(", ");
|
|
60
62
|
const rowPlaceholder = `(${table.fields.map(() => "?").join(", ")})`;
|
|
61
63
|
return `insert into ${table.name} (${columns}) VALUES ${Array.from({
|
|
62
64
|
length: rows,
|
|
@@ -64,6 +66,9 @@ const createBatchInsertSQL = (table, rows) => {
|
|
|
64
66
|
.map(() => rowPlaceholder)
|
|
65
67
|
.join(", ")};`;
|
|
66
68
|
};
|
|
69
|
+
const createInsertReturningSQL = (table) => `insert into ${table.name} (${table.fields.map((field) => escapeColumnName(field.name)).join(", ")}) VALUES (${table.fields.map((_x) => "?").join(", ")}) RETURNING ${table.primary};`;
|
|
70
|
+
const createInsertKnownIdSQL = (table) => `insert into ${table.name} (${table.fields.map((field) => escapeColumnName(field.name)).join(", ")}) VALUES (${table.fields.map((_x) => "?").join(", ")});`;
|
|
71
|
+
const createReplaceSQL = (table) => `insert or replace into ${table.name} (${table.fields.map((field) => escapeColumnName(field.name)).join(", ")}) VALUES (${table.fields.map((_x) => "?").join(", ")});`;
|
|
67
72
|
const canUseWithoutRowId = (table) => {
|
|
68
73
|
if (table.inline || table.primary === false || !table.primaryField) {
|
|
69
74
|
return false;
|
|
@@ -103,12 +108,13 @@ export class SQLiteIndex {
|
|
|
103
108
|
primaryKeyString;
|
|
104
109
|
planner;
|
|
105
110
|
scopeString;
|
|
106
|
-
_rootTables;
|
|
107
|
-
_tables;
|
|
108
|
-
_cursor; // TODO choose limit better
|
|
111
|
+
_rootTables = [];
|
|
112
|
+
_tables = new Map();
|
|
113
|
+
_cursor = new Map(); // TODO choose limit better
|
|
109
114
|
cursorPruner;
|
|
110
115
|
iteratorTimeout;
|
|
111
116
|
closed = true;
|
|
117
|
+
state = "closed";
|
|
112
118
|
fkMode;
|
|
113
119
|
id;
|
|
114
120
|
constructor(properties, options) {
|
|
@@ -128,21 +134,75 @@ export class SQLiteIndex {
|
|
|
128
134
|
persisted() {
|
|
129
135
|
return this.properties.persisted ?? true;
|
|
130
136
|
}
|
|
137
|
+
static _emptyTables = new Map();
|
|
138
|
+
static _emptyRootTables = [];
|
|
139
|
+
static _emptyCursor = new Map();
|
|
140
|
+
static closedIterator() {
|
|
141
|
+
return {
|
|
142
|
+
all: async () => [],
|
|
143
|
+
close: async () => undefined,
|
|
144
|
+
done: () => true,
|
|
145
|
+
next: async () => [],
|
|
146
|
+
pending: async () => 0,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async ifOpen(fallback, fn) {
|
|
150
|
+
if (this.isClosing()) {
|
|
151
|
+
return fallback;
|
|
152
|
+
}
|
|
153
|
+
this.assertOpen();
|
|
154
|
+
try {
|
|
155
|
+
return await fn();
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (this.isClosing()) {
|
|
159
|
+
return fallback;
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async withWriteIfOpen(fallback, fn) {
|
|
165
|
+
if (this.isClosing()) {
|
|
166
|
+
return fallback;
|
|
167
|
+
}
|
|
168
|
+
this.assertOpen();
|
|
169
|
+
return this.withWriteBarrier(() => this.ifOpen(fallback, fn));
|
|
170
|
+
}
|
|
171
|
+
assertOpen() {
|
|
172
|
+
if (this.state !== "open") {
|
|
173
|
+
throw new types.NotStartedError();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
isClosing() {
|
|
177
|
+
return this.state === "closing";
|
|
178
|
+
}
|
|
179
|
+
setClosing() {
|
|
180
|
+
this.state = "closing";
|
|
181
|
+
this.closed = true;
|
|
182
|
+
}
|
|
183
|
+
setClosed() {
|
|
184
|
+
this.state = "closed";
|
|
185
|
+
this.closed = true;
|
|
186
|
+
}
|
|
187
|
+
setOpen() {
|
|
188
|
+
this.state = "open";
|
|
189
|
+
this.closed = false;
|
|
190
|
+
}
|
|
131
191
|
get tables() {
|
|
132
192
|
if (this.closed) {
|
|
133
|
-
|
|
193
|
+
return SQLiteIndex._emptyTables;
|
|
134
194
|
}
|
|
135
195
|
return this._tables;
|
|
136
196
|
}
|
|
137
197
|
get rootTables() {
|
|
138
198
|
if (this.closed) {
|
|
139
|
-
|
|
199
|
+
return SQLiteIndex._emptyRootTables;
|
|
140
200
|
}
|
|
141
201
|
return this._rootTables;
|
|
142
202
|
}
|
|
143
203
|
get cursor() {
|
|
144
204
|
if (this.closed) {
|
|
145
|
-
|
|
205
|
+
return SQLiteIndex._emptyCursor;
|
|
146
206
|
}
|
|
147
207
|
return this._cursor;
|
|
148
208
|
}
|
|
@@ -166,9 +226,12 @@ export class SQLiteIndex {
|
|
|
166
226
|
return this;
|
|
167
227
|
}
|
|
168
228
|
async start() {
|
|
169
|
-
if (this.
|
|
229
|
+
if (this.state === "open") {
|
|
170
230
|
return;
|
|
171
231
|
}
|
|
232
|
+
if (this.state === "closing") {
|
|
233
|
+
throw new types.NotStartedError();
|
|
234
|
+
}
|
|
172
235
|
if (this.primaryKeyArr == null || this.primaryKeyArr.length === 0) {
|
|
173
236
|
throw new Error("Not initialized");
|
|
174
237
|
}
|
|
@@ -179,6 +242,8 @@ export class SQLiteIndex {
|
|
|
179
242
|
false, undefined, false);
|
|
180
243
|
this._rootTables = tables.filter((x) => x.parent == null);
|
|
181
244
|
const allTables = tables;
|
|
245
|
+
const startupStatements = [];
|
|
246
|
+
const startupTableStatements = new Map();
|
|
182
247
|
for (const table of allTables) {
|
|
183
248
|
this._tables.set(table.name, table);
|
|
184
249
|
for (const child of table.children) {
|
|
@@ -193,7 +258,23 @@ export class SQLiteIndex {
|
|
|
193
258
|
? " strict, without rowid"
|
|
194
259
|
: " strict";
|
|
195
260
|
const sqlCreateTable = `create table if not exists ${table.name} (${[...table.fields, ...table.constraints].map((s) => s.definition).join(", ")})${tableOptions}`;
|
|
196
|
-
|
|
261
|
+
startupTableStatements.set(table.name, sqlCreateTable);
|
|
262
|
+
startupStatements.push({
|
|
263
|
+
id: putStatementKey(table),
|
|
264
|
+
sql: createInsertReturningSQL(table),
|
|
265
|
+
}, {
|
|
266
|
+
id: insertKnownIdStatementKey(table),
|
|
267
|
+
sql: createInsertKnownIdSQL(table),
|
|
268
|
+
}, {
|
|
269
|
+
id: replaceStatementKey(table),
|
|
270
|
+
sql: createReplaceSQL(table),
|
|
271
|
+
});
|
|
272
|
+
if (table.parent) {
|
|
273
|
+
startupStatements.push({
|
|
274
|
+
id: resolveChildrenStatement(table),
|
|
275
|
+
sql: selectChildren(table),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
197
278
|
/* const fieldsToIndex = table.fields.filter(
|
|
198
279
|
(field) =>
|
|
199
280
|
field.key !== ARRAY_INDEX_COLUMN && field.key !== table.primary,
|
|
@@ -226,17 +307,24 @@ export class SQLiteIndex {
|
|
|
226
307
|
}
|
|
227
308
|
}
|
|
228
309
|
} */
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
310
|
+
}
|
|
311
|
+
if (startupTableStatements.size > 0) {
|
|
312
|
+
const existingTables = await this.getExistingSQLiteObjects("table", [
|
|
313
|
+
...startupTableStatements.keys(),
|
|
314
|
+
]);
|
|
315
|
+
const missingTableStatements = [...startupTableStatements.entries()]
|
|
316
|
+
.filter(([tableName]) => !existingTables.has(tableName))
|
|
317
|
+
.map(([, sql]) => sql);
|
|
318
|
+
if (missingTableStatements.length > 0) {
|
|
319
|
+
await this.properties.db.exec(missingTableStatements.join(";"));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (this.properties.db.prepareMany) {
|
|
323
|
+
await this.properties.db.prepareMany(startupStatements);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
for (const statement of startupStatements) {
|
|
327
|
+
await this.properties.db.prepare(statement.sql, statement.id);
|
|
240
328
|
}
|
|
241
329
|
}
|
|
242
330
|
this.cursorPruner = setInterval(() => {
|
|
@@ -247,7 +335,22 @@ export class SQLiteIndex {
|
|
|
247
335
|
}
|
|
248
336
|
}
|
|
249
337
|
}, this.iteratorTimeout);
|
|
250
|
-
this.
|
|
338
|
+
this.setOpen();
|
|
339
|
+
}
|
|
340
|
+
async getExistingSQLiteObjects(type, names) {
|
|
341
|
+
if (names.length === 0) {
|
|
342
|
+
return new Set();
|
|
343
|
+
}
|
|
344
|
+
const sql = `select name from sqlite_master where type = ? and name in (${names
|
|
345
|
+
.map(() => "?")
|
|
346
|
+
.join(", ")})`;
|
|
347
|
+
const statement = this.properties.db.statements.get(sql) ||
|
|
348
|
+
(await this.properties.db.prepare(sql, sql));
|
|
349
|
+
const rows = await statement.all([type, ...names]);
|
|
350
|
+
await statement.reset?.();
|
|
351
|
+
return new Set(rows
|
|
352
|
+
.map((row) => row.name)
|
|
353
|
+
.filter((name) => typeof name === "string"));
|
|
251
354
|
}
|
|
252
355
|
async clearStatements() {
|
|
253
356
|
if ((await this.properties.db.status()) === "closed") {
|
|
@@ -256,54 +359,86 @@ export class SQLiteIndex {
|
|
|
256
359
|
}
|
|
257
360
|
}
|
|
258
361
|
async stop() {
|
|
259
|
-
if (this.closed) {
|
|
362
|
+
if (this.state === "closed") {
|
|
260
363
|
return;
|
|
261
364
|
}
|
|
262
|
-
this.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
this._tables.clear();
|
|
266
|
-
for (const [k, _v] of this._cursor) {
|
|
267
|
-
await this.clearupIterator(k);
|
|
268
|
-
}
|
|
269
|
-
await this.planner.stop();
|
|
270
|
-
}
|
|
271
|
-
async drop() {
|
|
272
|
-
if (!this.closed) {
|
|
273
|
-
this.closed = true;
|
|
365
|
+
if (this.state === "closing") {
|
|
366
|
+
await this._writeBarrier.catch(() => undefined);
|
|
367
|
+
return;
|
|
274
368
|
}
|
|
275
|
-
|
|
369
|
+
this.setClosing();
|
|
370
|
+
try {
|
|
276
371
|
clearInterval(this.cursorPruner);
|
|
277
|
-
this.
|
|
372
|
+
await this._writeBarrier.catch(() => undefined);
|
|
373
|
+
await this.clearStatements();
|
|
374
|
+
this._tables?.clear();
|
|
375
|
+
if (this._cursor) {
|
|
376
|
+
for (const [k, _v] of this._cursor) {
|
|
377
|
+
await this.clearupIterator(k);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
await this.planner.stop();
|
|
278
381
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
this._tables.clear();
|
|
282
|
-
return;
|
|
382
|
+
finally {
|
|
383
|
+
this.setClosed();
|
|
283
384
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
385
|
+
}
|
|
386
|
+
async drop() {
|
|
387
|
+
const wasOpen = this.state === "open";
|
|
388
|
+
if (wasOpen) {
|
|
389
|
+
this.setClosing();
|
|
289
390
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
391
|
+
try {
|
|
392
|
+
if (this.cursorPruner != null) {
|
|
393
|
+
clearInterval(this.cursorPruner);
|
|
394
|
+
this.cursorPruner = undefined;
|
|
395
|
+
}
|
|
396
|
+
if (wasOpen) {
|
|
397
|
+
await this._writeBarrier.catch(() => undefined);
|
|
398
|
+
}
|
|
399
|
+
const status = await this.properties.db.status?.();
|
|
400
|
+
if (status === "closed") {
|
|
401
|
+
this._tables?.clear();
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
await this.clearStatements();
|
|
405
|
+
// drop root table and cascade
|
|
406
|
+
// drop table faster by dropping constraints first
|
|
407
|
+
if (this._rootTables) {
|
|
408
|
+
for (const table of this._rootTables) {
|
|
409
|
+
await this.properties.db.exec(`drop table if exists ${table.name}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
this._tables?.clear();
|
|
413
|
+
if (this._cursor) {
|
|
414
|
+
for (const [k, _v] of this._cursor) {
|
|
415
|
+
await this.clearupIterator(k);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
await this.planner.stop();
|
|
419
|
+
}
|
|
420
|
+
finally {
|
|
421
|
+
this.setClosed();
|
|
293
422
|
}
|
|
294
|
-
await this.planner.stop();
|
|
295
423
|
}
|
|
296
424
|
async resolveDependencies(parentId, table) {
|
|
297
|
-
const stmt = this.
|
|
425
|
+
const stmt = await this.getOrPrepareStatement(resolveChildrenStatement(table), selectChildren(table));
|
|
298
426
|
const results = await stmt.all([parentId]);
|
|
299
427
|
await stmt.reset?.();
|
|
300
428
|
return results;
|
|
301
429
|
}
|
|
430
|
+
async getOrPrepareStatement(key, sql) {
|
|
431
|
+
const existing = this.properties.db.statements.get(key);
|
|
432
|
+
if (existing) {
|
|
433
|
+
return existing;
|
|
434
|
+
}
|
|
435
|
+
return this.properties.db.prepare(sql, key);
|
|
436
|
+
}
|
|
302
437
|
async get(id, options) {
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
438
|
+
return this.ifOpen(undefined, async () => {
|
|
439
|
+
for (const table of this._rootTables) {
|
|
440
|
+
const { join: joinMap, selects } = selectAllFieldsFromTable(table, options?.shape);
|
|
441
|
+
const sql = `${generateSelectQuery(table, selects)} ${buildJoin(joinMap).join} where ${table.name}.${this.primaryKeyString} = ? limit 1`;
|
|
307
442
|
const stmt = await this.properties.db.prepare(sql, sql);
|
|
308
443
|
const rows = await stmt.get([
|
|
309
444
|
table.primaryField?.from?.type
|
|
@@ -318,17 +453,11 @@ export class SQLiteIndex {
|
|
|
318
453
|
id,
|
|
319
454
|
};
|
|
320
455
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
throw new types.NotStartedError();
|
|
324
|
-
}
|
|
325
|
-
throw error;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return undefined;
|
|
456
|
+
return undefined;
|
|
457
|
+
});
|
|
329
458
|
}
|
|
330
459
|
async put(value, _id, options) {
|
|
331
|
-
return this.
|
|
460
|
+
return this.withWriteIfOpen(undefined, async () => {
|
|
332
461
|
const classOfValue = value.constructor;
|
|
333
462
|
return insert(async (values, table) => {
|
|
334
463
|
let preId = values[table.primaryIndex];
|
|
@@ -337,7 +466,7 @@ export class SQLiteIndex {
|
|
|
337
466
|
if (preId != null) {
|
|
338
467
|
const shouldReplace = options?.replace ?? true;
|
|
339
468
|
if (!shouldReplace) {
|
|
340
|
-
statement = this.
|
|
469
|
+
statement = await this.getOrPrepareStatement(insertKnownIdStatementKey(table), createInsertKnownIdSQL(table));
|
|
341
470
|
try {
|
|
342
471
|
this.fkMode === "race-tolerant"
|
|
343
472
|
? await runIgnoreFK(statement, values)
|
|
@@ -348,14 +477,14 @@ export class SQLiteIndex {
|
|
|
348
477
|
throw error;
|
|
349
478
|
}
|
|
350
479
|
await statement.reset?.();
|
|
351
|
-
statement = this.
|
|
480
|
+
statement = await this.getOrPrepareStatement(replaceStatementKey(table), createReplaceSQL(table));
|
|
352
481
|
this.fkMode === "race-tolerant"
|
|
353
482
|
? await runIgnoreFK(statement, values)
|
|
354
483
|
: await statement.run(values);
|
|
355
484
|
}
|
|
356
485
|
}
|
|
357
486
|
else {
|
|
358
|
-
statement = this.
|
|
487
|
+
statement = await this.getOrPrepareStatement(replaceStatementKey(table), createReplaceSQL(table));
|
|
359
488
|
this.fkMode === "race-tolerant"
|
|
360
489
|
? await runIgnoreFK(statement, values)
|
|
361
490
|
: await statement.run(values);
|
|
@@ -363,7 +492,7 @@ export class SQLiteIndex {
|
|
|
363
492
|
return preId;
|
|
364
493
|
}
|
|
365
494
|
else {
|
|
366
|
-
statement = this.
|
|
495
|
+
statement = await this.getOrPrepareStatement(putStatementKey(table), createInsertReturningSQL(table));
|
|
367
496
|
const out = this.fkMode === "race-tolerant"
|
|
368
497
|
? await getIgnoreFK(statement, values)
|
|
369
498
|
: await statement.get(values);
|
|
@@ -397,6 +526,10 @@ export class SQLiteIndex {
|
|
|
397
526
|
});
|
|
398
527
|
}
|
|
399
528
|
iterate(request, options) {
|
|
529
|
+
if (this.isClosing()) {
|
|
530
|
+
return SQLiteIndex.closedIterator();
|
|
531
|
+
}
|
|
532
|
+
this.assertOpen();
|
|
400
533
|
// create a sql statement where the offset and the limit id dynamic and can be updated
|
|
401
534
|
// TODO don't use offset but sort and limit 'next' calls by the last value of the sort
|
|
402
535
|
/* const totalCountKey = "count"; */
|
|
@@ -417,54 +550,72 @@ export class SQLiteIndex {
|
|
|
417
550
|
let planningScope;
|
|
418
551
|
/* let totalCount: undefined | number = undefined; */
|
|
419
552
|
const fetch = async (amount) => {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
sqlFetch = sql;
|
|
429
|
-
bindable = toBind;
|
|
430
|
-
await planningScope.beforePrepare();
|
|
431
|
-
stmt = await this.properties.db.prepare(sqlFetch, sqlFetch);
|
|
432
|
-
// Bump timeout timer
|
|
433
|
-
iterator.expire = Date.now() + this.iteratorTimeout;
|
|
553
|
+
const closeAsDone = () => {
|
|
554
|
+
once = true;
|
|
555
|
+
hasMore = false;
|
|
556
|
+
kept = 0;
|
|
557
|
+
return [];
|
|
558
|
+
};
|
|
559
|
+
if (this.isClosing()) {
|
|
560
|
+
return closeAsDone();
|
|
434
561
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
562
|
+
this.assertOpen();
|
|
563
|
+
try {
|
|
564
|
+
kept = undefined;
|
|
565
|
+
if (!once) {
|
|
566
|
+
planningScope = this.planner.scope(normalizedQuery);
|
|
567
|
+
let { sql, bindable: toBind } = convertSearchRequestToQuery(normalizedQuery, this.tables, this._rootTables, {
|
|
568
|
+
planner: planningScope,
|
|
569
|
+
shape: options?.shape,
|
|
570
|
+
fetchAll: amount === "all", // if we are to fetch all, we dont need stable sorting
|
|
571
|
+
});
|
|
572
|
+
sqlFetch = sql;
|
|
573
|
+
bindable = toBind;
|
|
574
|
+
await planningScope.beforePrepare();
|
|
575
|
+
stmt = await this.properties.db.prepare(sqlFetch, sqlFetch);
|
|
576
|
+
// Bump timeout timer
|
|
577
|
+
iterator.expire = Date.now() + this.iteratorTimeout;
|
|
578
|
+
}
|
|
579
|
+
once = true;
|
|
580
|
+
const allResults = await planningScope.perform(async () => {
|
|
581
|
+
const allResults = await stmt.all([
|
|
582
|
+
...bindable,
|
|
583
|
+
...(amount !== "all" ? [amount, offset] : []),
|
|
584
|
+
]);
|
|
585
|
+
return allResults;
|
|
586
|
+
});
|
|
587
|
+
/* const allResults: Record<string, any>[] = await stmt.all([
|
|
438
588
|
...bindable,
|
|
439
|
-
...(amount !== "all" ? [amount,
|
|
589
|
+
...(amount !== "all" ? [amount,
|
|
590
|
+
offset] : [])
|
|
440
591
|
]);
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
592
|
+
*/
|
|
593
|
+
let results = await Promise.all(allResults.map(async (row) => {
|
|
594
|
+
let selectedTable = this._rootTables.find((table) => row[getTablePrefixedField(table, this.primaryKeyString)] !=
|
|
595
|
+
null);
|
|
596
|
+
const value = await resolveInstanceFromValue(row, this.tables, selectedTable, this.resolveDependencies.bind(this), true, options?.shape);
|
|
597
|
+
return {
|
|
598
|
+
value,
|
|
599
|
+
id: types.toId(convertFromSQLType(row[getTablePrefixedField(selectedTable, this.primaryKeyString)], selectedTable.primaryField.from.type)),
|
|
600
|
+
};
|
|
601
|
+
}));
|
|
602
|
+
offset += results.length;
|
|
603
|
+
/* const uniqueIds = new Set(results.map((x) => x.id.primitive));
|
|
604
|
+
if (uniqueIds.size !== results.length) {
|
|
605
|
+
throw new Error("Duplicate ids in result set");
|
|
606
|
+
} */
|
|
607
|
+
if (amount === "all" || results.length < amount) {
|
|
608
|
+
hasMore = false;
|
|
609
|
+
await this.clearupIterator(requestId);
|
|
610
|
+
}
|
|
611
|
+
return results;
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
if (this.isClosing()) {
|
|
615
|
+
return closeAsDone();
|
|
616
|
+
}
|
|
617
|
+
throw error;
|
|
466
618
|
}
|
|
467
|
-
return results;
|
|
468
619
|
};
|
|
469
620
|
const iterator = {
|
|
470
621
|
fetch,
|
|
@@ -487,12 +638,20 @@ export class SQLiteIndex {
|
|
|
487
638
|
return results;
|
|
488
639
|
},
|
|
489
640
|
close: () => {
|
|
641
|
+
once = true;
|
|
490
642
|
hasMore = false;
|
|
491
643
|
kept = 0;
|
|
492
644
|
this.clearupIterator(requestId);
|
|
493
645
|
},
|
|
494
646
|
next: (amount) => fetch(amount),
|
|
495
647
|
pending: async () => {
|
|
648
|
+
if (this.isClosing()) {
|
|
649
|
+
once = true;
|
|
650
|
+
hasMore = false;
|
|
651
|
+
kept = 0;
|
|
652
|
+
return 0;
|
|
653
|
+
}
|
|
654
|
+
this.assertOpen();
|
|
496
655
|
if (!hasMore) {
|
|
497
656
|
return 0;
|
|
498
657
|
}
|
|
@@ -504,7 +663,12 @@ export class SQLiteIndex {
|
|
|
504
663
|
hasMore = kept > 0;
|
|
505
664
|
return kept;
|
|
506
665
|
},
|
|
507
|
-
done: () =>
|
|
666
|
+
done: () => {
|
|
667
|
+
if (this.isClosing()) {
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
return once ? !hasMore : undefined;
|
|
671
|
+
},
|
|
508
672
|
};
|
|
509
673
|
}
|
|
510
674
|
async clearupIterator(id) {
|
|
@@ -517,25 +681,33 @@ export class SQLiteIndex {
|
|
|
517
681
|
this._cursor.delete(id);
|
|
518
682
|
}
|
|
519
683
|
async getSize() {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
684
|
+
return this.ifOpen(0, async () => {
|
|
685
|
+
if (this.tables.size === 0) {
|
|
686
|
+
return 0;
|
|
687
|
+
}
|
|
688
|
+
/* const stmt = await this.properties.db.prepare(`select count(*) as total from ${this.rootTableName}`);
|
|
689
|
+
const result = await stmt.get()
|
|
690
|
+
stmt.finalize?.();
|
|
691
|
+
return result.total as number */
|
|
692
|
+
return this.count();
|
|
693
|
+
});
|
|
528
694
|
}
|
|
529
695
|
async del(query) {
|
|
530
|
-
return this.
|
|
696
|
+
return this.withWriteIfOpen([], async () => {
|
|
531
697
|
let ret = [];
|
|
532
698
|
let once = false;
|
|
533
699
|
let lastError = undefined;
|
|
534
700
|
for (const table of this._rootTables) {
|
|
535
701
|
try {
|
|
536
|
-
const
|
|
702
|
+
const planningScope = this.planner.scope(new PlannableQuery({
|
|
703
|
+
query: coerceLocalQueries(query.query),
|
|
704
|
+
}));
|
|
705
|
+
const { sql, bindable } = convertDeleteRequestToQuery(query, this.tables, table, {
|
|
706
|
+
planner: planningScope,
|
|
707
|
+
});
|
|
708
|
+
await planningScope.beforePrepare();
|
|
537
709
|
const stmt = await this.properties.db.prepare(sql, sql);
|
|
538
|
-
const results = await stmt.all(bindable);
|
|
710
|
+
const results = await planningScope.perform(async () => stmt.all(bindable));
|
|
539
711
|
// TODO types
|
|
540
712
|
for (const result of results) {
|
|
541
713
|
ret.push(types.toId(convertFromSQLType(result[table.primary], table.primaryField.from.type)));
|
|
@@ -557,73 +729,83 @@ export class SQLiteIndex {
|
|
|
557
729
|
});
|
|
558
730
|
}
|
|
559
731
|
async sum(query) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const { sql, bindable } = convertSumRequestToQuery(query, this.tables, table);
|
|
572
|
-
const stmt = await this.properties.db.prepare(sql, sql);
|
|
573
|
-
const result = await stmt.get(bindable);
|
|
574
|
-
if (result != null) {
|
|
575
|
-
const value = result.sum;
|
|
576
|
-
if (ret == null) {
|
|
577
|
-
ret = value;
|
|
732
|
+
return this.ifOpen(0, async () => {
|
|
733
|
+
let ret = undefined;
|
|
734
|
+
let once = false;
|
|
735
|
+
let lastError = undefined;
|
|
736
|
+
let inlinedName = getInlineTableFieldName(query.key);
|
|
737
|
+
for (const table of this._rootTables) {
|
|
738
|
+
try {
|
|
739
|
+
if (table.fields.find((x) => x.name === inlinedName) == null) {
|
|
740
|
+
lastError = new MissingFieldError("Missing field: " +
|
|
741
|
+
(Array.isArray(query.key) ? query.key : [query.key]).join("."));
|
|
742
|
+
continue;
|
|
578
743
|
}
|
|
579
|
-
|
|
580
|
-
|
|
744
|
+
const planningScope = this.planner.scope(new PlannableQuery({
|
|
745
|
+
query: coerceLocalQueries(query.query),
|
|
746
|
+
}));
|
|
747
|
+
const { sql, bindable } = convertSumRequestToQuery(query, this.tables, table, {
|
|
748
|
+
planner: planningScope,
|
|
749
|
+
});
|
|
750
|
+
await planningScope.beforePrepare();
|
|
751
|
+
const stmt = await this.properties.db.prepare(sql, sql);
|
|
752
|
+
const result = await planningScope.perform(async () => stmt.get(bindable));
|
|
753
|
+
if (result != null) {
|
|
754
|
+
const value = result.sum;
|
|
755
|
+
if (ret == null) {
|
|
756
|
+
ret = value;
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
ret += value;
|
|
760
|
+
}
|
|
761
|
+
once = true;
|
|
581
762
|
}
|
|
582
|
-
once = true;
|
|
583
763
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
764
|
+
catch (error) {
|
|
765
|
+
if (error instanceof MissingFieldError) {
|
|
766
|
+
lastError = error;
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
throw error;
|
|
589
770
|
}
|
|
590
|
-
throw error;
|
|
591
771
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
772
|
+
if (!once) {
|
|
773
|
+
throw lastError;
|
|
774
|
+
}
|
|
775
|
+
return ret != null ? ret : 0;
|
|
776
|
+
});
|
|
597
777
|
}
|
|
598
778
|
async count(request) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
779
|
+
return this.ifOpen(0, async () => {
|
|
780
|
+
let ret = 0;
|
|
781
|
+
let once = false;
|
|
782
|
+
let lastError = undefined;
|
|
783
|
+
for (const table of this._rootTables) {
|
|
784
|
+
try {
|
|
785
|
+
const { sql, bindable } = convertCountRequestToQuery(request, this.tables, table);
|
|
786
|
+
const stmt = await this.properties.db.prepare(sql, sql);
|
|
787
|
+
const result = await stmt.get(bindable);
|
|
788
|
+
if (result != null) {
|
|
789
|
+
ret += Number(result.count);
|
|
790
|
+
once = true;
|
|
791
|
+
}
|
|
610
792
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
793
|
+
catch (error) {
|
|
794
|
+
if (error instanceof MissingFieldError) {
|
|
795
|
+
lastError = error;
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
throw error;
|
|
616
799
|
}
|
|
617
|
-
throw error;
|
|
618
800
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
801
|
+
if (!once) {
|
|
802
|
+
throw lastError;
|
|
803
|
+
}
|
|
804
|
+
return ret;
|
|
805
|
+
});
|
|
624
806
|
}
|
|
625
807
|
get cursorCount() {
|
|
626
|
-
return this.
|
|
808
|
+
return this.closed ? 0 : this._cursor.size;
|
|
627
809
|
}
|
|
628
810
|
}
|
|
629
811
|
export class SQLiteIndices {
|
|
@@ -683,7 +865,10 @@ export class SQLiteIndices {
|
|
|
683
865
|
async start() {
|
|
684
866
|
this.closed = false;
|
|
685
867
|
if (!this.properties.parent) {
|
|
686
|
-
await this.properties.db.
|
|
868
|
+
const status = await this.properties.db.status();
|
|
869
|
+
if (status !== "open") {
|
|
870
|
+
await this.properties.db.open();
|
|
871
|
+
}
|
|
687
872
|
}
|
|
688
873
|
for (const scope of this.scopes.values()) {
|
|
689
874
|
await scope.start();
|