@peerbit/indexer-sqlite3 1.1.4 → 1.2.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/dist/peerbit/sqlite3-bundler-friendly.mjs +7 -7
- package/dist/peerbit/sqlite3-node.mjs +7 -7
- package/dist/peerbit/sqlite3.js +7 -7
- package/dist/peerbit/sqlite3.min.js +688 -168
- package/dist/peerbit/sqlite3.mjs +7 -7
- package/dist/peerbit/sqlite3.wasm +0 -0
- package/dist/peerbit/sqlite3.worker.min.js +19 -5
- package/dist/src/engine.d.ts +4 -1
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +125 -48
- package/dist/src/engine.js.map +1 -1
- package/dist/src/query-planner.d.ts +47 -0
- package/dist/src/query-planner.d.ts.map +1 -0
- package/dist/src/query-planner.js +290 -0
- package/dist/src/query-planner.js.map +1 -0
- package/dist/src/schema.d.ts +31 -7
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +370 -123
- package/dist/src/schema.js.map +1 -1
- package/dist/src/sqlite3-messages.worker.d.ts +4 -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 +7 -0
- package/dist/src/sqlite3.browser.js.map +1 -1
- package/dist/src/sqlite3.d.ts.map +1 -1
- package/dist/src/sqlite3.js +24 -14
- package/dist/src/sqlite3.js.map +1 -1
- package/dist/src/sqlite3.wasm.d.ts +1 -0
- package/dist/src/sqlite3.wasm.d.ts.map +1 -1
- package/dist/src/sqlite3.wasm.js +9 -1
- package/dist/src/sqlite3.wasm.js.map +1 -1
- package/dist/src/sqlite3.worker.js +7 -0
- package/dist/src/sqlite3.worker.js.map +1 -1
- package/dist/src/types.d.ts +1 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/engine.ts +143 -68
- package/src/query-planner.ts +334 -0
- package/src/schema.ts +519 -164
- package/src/sqlite3-messages.worker.ts +5 -0
- package/src/sqlite3.browser.ts +8 -0
- package/src/sqlite3.ts +24 -13
- package/src/sqlite3.wasm.ts +11 -1
- package/src/sqlite3.worker.ts +6 -1
- package/src/types.ts +1 -0
package/src/engine.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from "@peerbit/indexer-interface";
|
|
8
8
|
import * as types from "@peerbit/indexer-interface";
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
|
+
import { PlannableQuery, QueryPlanner } from "./query-planner.js";
|
|
10
11
|
import {
|
|
11
12
|
MissingFieldError,
|
|
12
13
|
type Table,
|
|
@@ -14,6 +15,9 @@ import {
|
|
|
14
15
|
convertCountRequestToQuery,
|
|
15
16
|
convertDeleteRequestToQuery,
|
|
16
17
|
convertFromSQLType,
|
|
18
|
+
/* convertFromSQLType, */
|
|
19
|
+
|
|
20
|
+
/* convertFromSQLType, */
|
|
17
21
|
convertSearchRequestToQuery,
|
|
18
22
|
/* getTableName, */
|
|
19
23
|
convertSumRequestToQuery,
|
|
@@ -45,6 +49,7 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
45
49
|
{
|
|
46
50
|
primaryKeyArr!: string[];
|
|
47
51
|
primaryKeyString!: string;
|
|
52
|
+
planner: QueryPlanner;
|
|
48
53
|
private scopeString?: string;
|
|
49
54
|
private _rootTables!: Table[];
|
|
50
55
|
private _tables!: Map<string, Table>;
|
|
@@ -53,9 +58,10 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
53
58
|
{
|
|
54
59
|
fetch: (amount: number) => Promise<IndexedResult[]>;
|
|
55
60
|
/* countStatement: Statement; */
|
|
56
|
-
|
|
61
|
+
expire: number;
|
|
57
62
|
}
|
|
58
63
|
>; // TODO choose limit better
|
|
64
|
+
private cursorPruner: ReturnType<typeof setInterval> | undefined;
|
|
59
65
|
|
|
60
66
|
iteratorTimeout: number;
|
|
61
67
|
closed: boolean = true;
|
|
@@ -78,25 +84,28 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
78
84
|
? "_" + escapePathToSQLName(properties.scope).join("_")
|
|
79
85
|
: undefined;
|
|
80
86
|
this.iteratorTimeout = options?.iteratorTimeout || 60e3;
|
|
87
|
+
this.planner = new QueryPlanner({
|
|
88
|
+
exec: this.properties.db.exec.bind(this.properties.db),
|
|
89
|
+
});
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
get tables() {
|
|
84
93
|
if (this.closed) {
|
|
85
|
-
throw new
|
|
94
|
+
throw new types.NotStartedError();
|
|
86
95
|
}
|
|
87
96
|
return this._tables;
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
get rootTables() {
|
|
91
100
|
if (this.closed) {
|
|
92
|
-
throw new
|
|
101
|
+
throw new types.NotStartedError();
|
|
93
102
|
}
|
|
94
103
|
return this._rootTables;
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
get cursor() {
|
|
98
107
|
if (this.closed) {
|
|
99
|
-
throw new
|
|
108
|
+
throw new types.NotStartedError();
|
|
100
109
|
}
|
|
101
110
|
return this._cursor;
|
|
102
111
|
}
|
|
@@ -169,10 +178,40 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
const sqlCreateTable = `create table if not exists ${table.name} (${[...table.fields, ...table.constraints].map((s) => s.definition).join(", ")}) strict`;
|
|
172
|
-
const sqlCreateIndex = `create index if not exists ${table.name}_index on ${table.name} (${table.fields.map((field) => escapeColumnName(field.name)).join(", ")})`;
|
|
173
|
-
|
|
174
181
|
this.properties.db.exec(sqlCreateTable);
|
|
175
|
-
|
|
182
|
+
|
|
183
|
+
/* const fieldsToIndex = table.fields.filter(
|
|
184
|
+
(field) =>
|
|
185
|
+
field.key !== ARRAY_INDEX_COLUMN && field.key !== table.primary,
|
|
186
|
+
);
|
|
187
|
+
if (fieldsToIndex.length > 0) {
|
|
188
|
+
let arr = fieldsToIndex.map((field) => escapeColumnName(field.name));
|
|
189
|
+
|
|
190
|
+
const createIndex = async (columns: string[]) => {
|
|
191
|
+
const key = createIndexKey(table.name, columns)
|
|
192
|
+
const command = `create index if not exists ${key} on ${table.name} (${columns.map((n) => escapeColumnName(n)).join(", ")})`;
|
|
193
|
+
await this.properties.db.exec(command);
|
|
194
|
+
table.indices.add(key);
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
const rev = columns.reverse()
|
|
199
|
+
const key2 = createIndexKey(table.name, rev)
|
|
200
|
+
const command2 = `create index if not exists ${key2} on ${table.name} (${rev.join(", ")})`;
|
|
201
|
+
await this.properties.db.exec(command2);
|
|
202
|
+
table.indices.add(key2);
|
|
203
|
+
}
|
|
204
|
+
await createIndex(fieldsToIndex.map(x => x.name));
|
|
205
|
+
await createIndex([table.primary as string, ...fieldsToIndex.map(x => x.name)]);
|
|
206
|
+
|
|
207
|
+
if (arr.length > 1) {
|
|
208
|
+
for (const field of fieldsToIndex) {
|
|
209
|
+
await createIndex([field.name]);
|
|
210
|
+
await createIndex([table.primary as string, field.name]);
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} */
|
|
176
215
|
|
|
177
216
|
// put and return the id
|
|
178
217
|
let sqlPut = `insert into ${table.name} (${table.fields.map((field) => escapeColumnName(field.name)).join(", ")}) VALUES (${table.fields.map((_x) => "?").join(", ")}) RETURNING ${table.primary};`;
|
|
@@ -182,6 +221,7 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
182
221
|
|
|
183
222
|
await this.properties.db.prepare(sqlPut, putStatementKey(table));
|
|
184
223
|
await this.properties.db.prepare(sqlReplace, replaceStatementKey(table));
|
|
224
|
+
|
|
185
225
|
if (table.parent) {
|
|
186
226
|
await this.properties.db.prepare(
|
|
187
227
|
selectChildren(table),
|
|
@@ -190,6 +230,15 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
190
230
|
}
|
|
191
231
|
}
|
|
192
232
|
|
|
233
|
+
this.cursorPruner = setInterval(() => {
|
|
234
|
+
const now = Date.now();
|
|
235
|
+
for (const [k, v] of this._cursor) {
|
|
236
|
+
if (v.expire < now) {
|
|
237
|
+
this.clearupIterator(k);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}, this.iteratorTimeout);
|
|
241
|
+
|
|
193
242
|
this.closed = false;
|
|
194
243
|
}
|
|
195
244
|
|
|
@@ -205,6 +254,7 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
205
254
|
return;
|
|
206
255
|
}
|
|
207
256
|
this.closed = true;
|
|
257
|
+
clearInterval(this.cursorPruner!);
|
|
208
258
|
|
|
209
259
|
await this.clearStatements();
|
|
210
260
|
|
|
@@ -213,15 +263,23 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
213
263
|
for (const [k, _v] of this._cursor) {
|
|
214
264
|
await this.clearupIterator(k);
|
|
215
265
|
}
|
|
266
|
+
|
|
267
|
+
await this.planner.stop();
|
|
216
268
|
}
|
|
217
269
|
|
|
218
270
|
async drop(): Promise<void> {
|
|
271
|
+
if (this.closed) {
|
|
272
|
+
throw new Error(`Already closed index ${this.id}, can not drop`);
|
|
273
|
+
}
|
|
274
|
+
|
|
219
275
|
this.closed = true;
|
|
276
|
+
clearInterval(this.cursorPruner!);
|
|
220
277
|
|
|
221
278
|
await this.clearStatements();
|
|
222
279
|
|
|
223
280
|
// drop root table and cascade
|
|
224
281
|
// drop table faster by dropping constraints first
|
|
282
|
+
|
|
225
283
|
for (const table of this._rootTables) {
|
|
226
284
|
await this.properties.db.exec(`drop table if exists ${table.name}`);
|
|
227
285
|
}
|
|
@@ -231,6 +289,7 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
231
289
|
for (const [k, _v] of this._cursor) {
|
|
232
290
|
await this.clearupIterator(k);
|
|
233
291
|
}
|
|
292
|
+
await this.planner.stop();
|
|
234
293
|
}
|
|
235
294
|
|
|
236
295
|
private async resolveDependencies(
|
|
@@ -253,27 +312,36 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
253
312
|
table,
|
|
254
313
|
options?.shape,
|
|
255
314
|
);
|
|
256
|
-
const sql = `${generateSelectQuery(table, selects)} ${buildJoin(joinMap
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
315
|
+
const sql = `${generateSelectQuery(table, selects)} ${buildJoin(joinMap).join} where ${this.primaryKeyString} = ? limit 1`;
|
|
316
|
+
try {
|
|
317
|
+
const stmt = await this.properties.db.prepare(sql, sql);
|
|
318
|
+
const rows = await stmt.get([
|
|
319
|
+
table.primaryField?.from?.type
|
|
320
|
+
? convertToSQLType(id.key, table.primaryField.from.type)
|
|
321
|
+
: id.key,
|
|
322
|
+
]);
|
|
323
|
+
if (
|
|
324
|
+
rows?.[getTablePrefixedField(table, table.primary as string)] == null
|
|
325
|
+
) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
value: (await resolveInstanceFromValue(
|
|
330
|
+
rows,
|
|
331
|
+
this.tables,
|
|
332
|
+
table,
|
|
333
|
+
this.resolveDependencies.bind(this),
|
|
334
|
+
true,
|
|
335
|
+
options?.shape,
|
|
336
|
+
)) as unknown as T,
|
|
337
|
+
id,
|
|
338
|
+
};
|
|
339
|
+
} catch (error) {
|
|
340
|
+
if (this.closed) {
|
|
341
|
+
throw new types.NotStartedError();
|
|
342
|
+
}
|
|
343
|
+
throw error;
|
|
265
344
|
}
|
|
266
|
-
return {
|
|
267
|
-
value: (await resolveInstanceFromValue(
|
|
268
|
-
rows,
|
|
269
|
-
this.tables,
|
|
270
|
-
table,
|
|
271
|
-
this.resolveDependencies.bind(this),
|
|
272
|
-
true,
|
|
273
|
-
options?.shape,
|
|
274
|
-
)) as unknown as T,
|
|
275
|
-
id,
|
|
276
|
-
};
|
|
277
345
|
}
|
|
278
346
|
return undefined;
|
|
279
347
|
}
|
|
@@ -282,24 +350,20 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
282
350
|
const classOfValue = value.constructor as Constructor<T>;
|
|
283
351
|
return insert(
|
|
284
352
|
async (values, table) => {
|
|
285
|
-
|
|
353
|
+
let preId = values[table.primaryIndex];
|
|
286
354
|
|
|
287
355
|
if (preId != null) {
|
|
288
356
|
const statement = this.properties.db.statements.get(
|
|
289
357
|
replaceStatementKey(table),
|
|
290
358
|
)!;
|
|
291
|
-
await statement.run(
|
|
292
|
-
values.map((x) => (typeof x === "boolean" ? (x ? 1 : 0) : x)),
|
|
293
|
-
);
|
|
359
|
+
await statement.run(values);
|
|
294
360
|
await statement.reset?.();
|
|
295
361
|
return preId;
|
|
296
362
|
} else {
|
|
297
363
|
const statement = this.properties.db.statements.get(
|
|
298
364
|
putStatementKey(table),
|
|
299
365
|
)!;
|
|
300
|
-
const out = await statement.get(
|
|
301
|
-
values.map((x) => (typeof x === "boolean" ? (x ? 1 : 0) : x)),
|
|
302
|
-
);
|
|
366
|
+
const out = await statement.get(values);
|
|
303
367
|
await statement.reset?.();
|
|
304
368
|
|
|
305
369
|
// TODO types
|
|
@@ -345,47 +409,63 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
345
409
|
let bindable: any[] = [];
|
|
346
410
|
let sqlFetch: string | undefined = undefined;
|
|
347
411
|
|
|
412
|
+
const normalizedQuery = new PlannableQuery({
|
|
413
|
+
query: types.toQuery(request?.query),
|
|
414
|
+
sort: request?.sort,
|
|
415
|
+
});
|
|
416
|
+
let planningScope: ReturnType<QueryPlanner["scope"]>;
|
|
417
|
+
|
|
348
418
|
/* let totalCount: undefined | number = undefined; */
|
|
349
419
|
const fetch = async (amount: number | "all") => {
|
|
350
420
|
kept = undefined;
|
|
351
421
|
if (!once) {
|
|
422
|
+
planningScope = this.planner.scope(normalizedQuery);
|
|
423
|
+
|
|
352
424
|
let { sql, bindable: toBind } = convertSearchRequestToQuery(
|
|
353
|
-
|
|
425
|
+
normalizedQuery,
|
|
354
426
|
this.tables,
|
|
355
427
|
this._rootTables,
|
|
356
428
|
{
|
|
429
|
+
planner: planningScope,
|
|
357
430
|
shape: options?.shape,
|
|
358
|
-
|
|
431
|
+
fetchAll: amount === "all", // if we are to fetch all, we dont need stable sorting
|
|
359
432
|
},
|
|
360
433
|
);
|
|
434
|
+
|
|
361
435
|
sqlFetch = sql;
|
|
362
436
|
bindable = toBind;
|
|
363
437
|
|
|
438
|
+
await planningScope.beforePrepare();
|
|
439
|
+
|
|
364
440
|
stmt = await this.properties.db.prepare(sqlFetch, sqlFetch);
|
|
365
|
-
// stmt.reset?.(); // TODO dont invoke reset if not needed
|
|
366
|
-
/* countStmt.reset?.(); */
|
|
367
441
|
|
|
368
442
|
// Bump timeout timer
|
|
369
|
-
|
|
370
|
-
iterator.timeout = setTimeout(
|
|
371
|
-
() => this.clearupIterator(requestId),
|
|
372
|
-
this.iteratorTimeout,
|
|
373
|
-
);
|
|
443
|
+
iterator.expire = Date.now() + this.iteratorTimeout;
|
|
374
444
|
}
|
|
375
445
|
|
|
376
446
|
once = true;
|
|
377
447
|
|
|
378
|
-
|
|
448
|
+
/* console.log("----------------------")
|
|
449
|
+
console.log(sqlFetch); */
|
|
450
|
+
const allResults = await planningScope.perform(async () => {
|
|
451
|
+
const allResults: Record<string, any>[] = await stmt.all([
|
|
452
|
+
...bindable,
|
|
453
|
+
...(amount !== "all" ? [amount, offset] : []),
|
|
454
|
+
]);
|
|
455
|
+
return allResults;
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
/* const allResults: Record<string, any>[] = await stmt.all([
|
|
379
459
|
...bindable,
|
|
380
|
-
amount
|
|
381
|
-
|
|
460
|
+
...(amount !== "all" ? [amount,
|
|
461
|
+
offset] : [])
|
|
382
462
|
]);
|
|
383
|
-
|
|
463
|
+
*/
|
|
384
464
|
let results: IndexedResult<types.ReturnTypeFromShape<T, S>>[] =
|
|
385
465
|
await Promise.all(
|
|
386
466
|
allResults.map(async (row: any) => {
|
|
387
467
|
let selectedTable = this._rootTables.find(
|
|
388
|
-
(table
|
|
468
|
+
(table) =>
|
|
389
469
|
row[getTablePrefixedField(table, this.primaryKeyString)] !=
|
|
390
470
|
null,
|
|
391
471
|
)!;
|
|
@@ -415,21 +495,14 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
415
495
|
|
|
416
496
|
offset += results.length;
|
|
417
497
|
|
|
418
|
-
/*
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
(await this.count(
|
|
422
|
-
request,
|
|
423
|
-
));
|
|
424
|
-
iterator.kept = totalCount - results.length - offsetStart;
|
|
425
|
-
} else {
|
|
426
|
-
iterator.kept = 0;
|
|
498
|
+
/* const uniqueIds = new Set(results.map((x) => x.id.primitive));
|
|
499
|
+
if (uniqueIds.size !== results.length) {
|
|
500
|
+
throw new Error("Duplicate ids in result set");
|
|
427
501
|
} */
|
|
428
502
|
|
|
429
503
|
if (amount === "all" || results.length < amount) {
|
|
430
504
|
hasMore = false;
|
|
431
505
|
await this.clearupIterator(requestId);
|
|
432
|
-
clearTimeout(iterator.timeout);
|
|
433
506
|
}
|
|
434
507
|
return results;
|
|
435
508
|
};
|
|
@@ -437,10 +510,7 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
437
510
|
const iterator = {
|
|
438
511
|
fetch,
|
|
439
512
|
/* countStatement: countStmt, */
|
|
440
|
-
|
|
441
|
-
() => this.clearupIterator(requestId),
|
|
442
|
-
this.iteratorTimeout,
|
|
443
|
-
),
|
|
513
|
+
expire: Date.now() + this.iteratorTimeout,
|
|
444
514
|
};
|
|
445
515
|
|
|
446
516
|
this.cursor.set(requestId, iterator);
|
|
@@ -450,9 +520,9 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
450
520
|
all: async () => {
|
|
451
521
|
const results: IndexedResult<types.ReturnTypeFromShape<T, S>>[] = [];
|
|
452
522
|
while (true) {
|
|
453
|
-
const res = await fetch(
|
|
523
|
+
const res = await fetch("all");
|
|
454
524
|
results.push(...res);
|
|
455
|
-
if (
|
|
525
|
+
if (hasMore === false) {
|
|
456
526
|
break;
|
|
457
527
|
}
|
|
458
528
|
}
|
|
@@ -486,7 +556,6 @@ export class SQLLiteIndex<T extends Record<string, any>>
|
|
|
486
556
|
if (!cache) {
|
|
487
557
|
return; // already cleared
|
|
488
558
|
}
|
|
489
|
-
clearTimeout(cache.timeout);
|
|
490
559
|
/* cache.countStatement.finalize?.(); */
|
|
491
560
|
// await cache.fetchStatement.finalize?.();
|
|
492
561
|
this._cursor.delete(id);
|
|
@@ -717,10 +786,16 @@ export class SQLiteIndices implements types.Indices {
|
|
|
717
786
|
await scope.drop();
|
|
718
787
|
}
|
|
719
788
|
|
|
720
|
-
|
|
721
|
-
|
|
789
|
+
if (!this.properties.parent) {
|
|
790
|
+
for (const index of this.indices) {
|
|
791
|
+
await index.index.stop();
|
|
792
|
+
}
|
|
793
|
+
await this.properties.db.drop();
|
|
794
|
+
} else {
|
|
795
|
+
for (const index of this.indices) {
|
|
796
|
+
await index.index.drop();
|
|
797
|
+
}
|
|
722
798
|
}
|
|
723
|
-
|
|
724
799
|
this.scopes.clear();
|
|
725
800
|
}
|
|
726
801
|
}
|