@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/schema.ts
CHANGED
|
@@ -14,8 +14,9 @@ import {
|
|
|
14
14
|
serialize,
|
|
15
15
|
variant,
|
|
16
16
|
} from "@dao-xyz/borsh";
|
|
17
|
-
import { toHexString } from "@peerbit/crypto";
|
|
17
|
+
import { fromHexString, toHexString } from "@peerbit/crypto";
|
|
18
18
|
import * as types from "@peerbit/indexer-interface";
|
|
19
|
+
import { type PlanningSession, flattenQuery } from "./query-planner.js";
|
|
19
20
|
|
|
20
21
|
const SQLConversionMap: any = {
|
|
21
22
|
u8: "INTEGER",
|
|
@@ -54,6 +55,15 @@ export type BindableValue =
|
|
|
54
55
|
| ArrayBuffer
|
|
55
56
|
| null;
|
|
56
57
|
|
|
58
|
+
let JSON_GROUP_ARRAY = "json_group_array";
|
|
59
|
+
let JSON_OBJECT = "distinct json_object";
|
|
60
|
+
|
|
61
|
+
export const u64ToI64 = (u64: bigint | number) => {
|
|
62
|
+
return (typeof u64 === "number" ? BigInt(u64) : u64) - 9223372036854775808n;
|
|
63
|
+
};
|
|
64
|
+
export const i64ToU64 = (i64: number | bigint) =>
|
|
65
|
+
(typeof i64 === "number" ? BigInt(i64) : i64) + 9223372036854775808n;
|
|
66
|
+
|
|
57
67
|
export const convertToSQLType = (
|
|
58
68
|
value: boolean | bigint | string | number | Uint8Array,
|
|
59
69
|
type?: FieldType,
|
|
@@ -64,12 +74,18 @@ export const convertToSQLType = (
|
|
|
64
74
|
if (type === "bool") {
|
|
65
75
|
return value ? 1 : 0;
|
|
66
76
|
}
|
|
77
|
+
if (type === "u64") {
|
|
78
|
+
// shift to fit in i64
|
|
79
|
+
|
|
80
|
+
return u64ToI64(value as number | bigint);
|
|
81
|
+
}
|
|
67
82
|
}
|
|
68
83
|
return value as BindableValue;
|
|
69
84
|
};
|
|
70
85
|
|
|
71
86
|
const nullAsUndefined = (value: any) => (value === null ? undefined : value);
|
|
72
|
-
export const escapeColumnName = (name: string) =>
|
|
87
|
+
export const escapeColumnName = (name: string, char = '"') =>
|
|
88
|
+
`${char}${name}${char}`;
|
|
73
89
|
|
|
74
90
|
export class MissingFieldError extends Error {
|
|
75
91
|
constructor(message: string) {
|
|
@@ -100,9 +116,15 @@ export const convertFromSQLType = (
|
|
|
100
116
|
: nullAsUndefined(value);
|
|
101
117
|
}
|
|
102
118
|
if (type === "u64") {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
119
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
120
|
+
return i64ToU64(value as number | bigint); // TODO is not always value type bigint?
|
|
121
|
+
}
|
|
122
|
+
if (value == null) {
|
|
123
|
+
return nullAsUndefined(value);
|
|
124
|
+
}
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Unexpected value type for value ${value} expected number or bigint for u64 field`,
|
|
127
|
+
);
|
|
106
128
|
}
|
|
107
129
|
return nullAsUndefined(value);
|
|
108
130
|
};
|
|
@@ -135,6 +157,7 @@ type SQLField = {
|
|
|
135
157
|
type: string;
|
|
136
158
|
isPrimary: boolean;
|
|
137
159
|
from: Field | undefined;
|
|
160
|
+
unwrappedType: FieldType | undefined;
|
|
138
161
|
path: string[];
|
|
139
162
|
describesExistenceOfAnother?: string;
|
|
140
163
|
};
|
|
@@ -155,6 +178,7 @@ export interface Table {
|
|
|
155
178
|
parent: Table | undefined;
|
|
156
179
|
referencedInArray: boolean;
|
|
157
180
|
isSimpleValue: boolean;
|
|
181
|
+
indices: Set<string>;
|
|
158
182
|
}
|
|
159
183
|
|
|
160
184
|
export const getSQLTable = (
|
|
@@ -203,6 +227,7 @@ export const getSQLTable = (
|
|
|
203
227
|
referencedInArray: false,
|
|
204
228
|
isSimpleValue: false,
|
|
205
229
|
inline,
|
|
230
|
+
indices: new Set<string>(),
|
|
206
231
|
};
|
|
207
232
|
ret.push(table);
|
|
208
233
|
for (const dep of dependencies) {
|
|
@@ -242,6 +267,14 @@ export const getTableName = (
|
|
|
242
267
|
path: string[] = [],
|
|
243
268
|
clazz: string | Constructor<any>,
|
|
244
269
|
) => {
|
|
270
|
+
let pathKey = path.length > 0 ? path.join("__") + "__" : "";
|
|
271
|
+
if (typeof clazz !== "string") {
|
|
272
|
+
const tableName = (clazz as any)["__table_" + pathKey];
|
|
273
|
+
if (tableName) {
|
|
274
|
+
return tableName;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
245
278
|
let name: string = typeof clazz === "string" ? clazz : getNameOfClass(clazz);
|
|
246
279
|
|
|
247
280
|
// prefix the generated table name so that the name is a valid SQL identifier (table name)
|
|
@@ -249,9 +282,11 @@ export const getTableName = (
|
|
|
249
282
|
|
|
250
283
|
// leading _ to allow path to have numbers
|
|
251
284
|
|
|
252
|
-
const ret =
|
|
253
|
-
|
|
254
|
-
|
|
285
|
+
const ret = pathKey + name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
286
|
+
|
|
287
|
+
if (typeof clazz !== "string") {
|
|
288
|
+
(clazz as any)["__table_" + pathKey] = ret;
|
|
289
|
+
}
|
|
255
290
|
return ret;
|
|
256
291
|
};
|
|
257
292
|
|
|
@@ -301,13 +336,14 @@ export const getSQLFields = (
|
|
|
301
336
|
? addJoinFieldFromParent
|
|
302
337
|
: (fields: SQLField[], contstraints: SQLConstraint[]) => {
|
|
303
338
|
// we resolve primary field here since it might be unknown until this point
|
|
304
|
-
const
|
|
339
|
+
const parentPrimaryField =
|
|
305
340
|
primary != null
|
|
306
341
|
? sqlFields.find((field) => field.name === primary)
|
|
307
342
|
: undefined;
|
|
308
|
-
const parentPrimaryFieldName =
|
|
309
|
-
|
|
310
|
-
|
|
343
|
+
const parentPrimaryFieldName =
|
|
344
|
+
parentPrimaryField?.key || CHILD_TABLE_ID;
|
|
345
|
+
const parentPrimaryFieldType = parentPrimaryField
|
|
346
|
+
? parentPrimaryField.type
|
|
311
347
|
: "INTEGER";
|
|
312
348
|
|
|
313
349
|
fields.unshift(
|
|
@@ -318,6 +354,7 @@ export const getSQLFields = (
|
|
|
318
354
|
type: "INTEGER",
|
|
319
355
|
isPrimary: true,
|
|
320
356
|
from: undefined,
|
|
357
|
+
unwrappedType: undefined,
|
|
321
358
|
path: [CHILD_TABLE_ID],
|
|
322
359
|
},
|
|
323
360
|
|
|
@@ -327,8 +364,9 @@ export const getSQLFields = (
|
|
|
327
364
|
key: PARENT_TABLE_ID,
|
|
328
365
|
definition: `${PARENT_TABLE_ID} ${parentPrimaryFieldType}`,
|
|
329
366
|
type: parentPrimaryFieldType,
|
|
367
|
+
from: parentPrimaryField?.from,
|
|
368
|
+
unwrappedType: parentPrimaryField?.unwrappedType,
|
|
330
369
|
isPrimary: false,
|
|
331
|
-
from: undefined,
|
|
332
370
|
path: [PARENT_TABLE_ID],
|
|
333
371
|
},
|
|
334
372
|
);
|
|
@@ -395,6 +433,7 @@ export const getSQLFields = (
|
|
|
395
433
|
type: "INTEGER",
|
|
396
434
|
isPrimary: false,
|
|
397
435
|
from: undefined,
|
|
436
|
+
unwrappedType: undefined,
|
|
398
437
|
path: [ARRAY_INDEX_COLUMN],
|
|
399
438
|
},
|
|
400
439
|
...table.fields.slice(2),
|
|
@@ -425,6 +464,7 @@ export const getSQLFields = (
|
|
|
425
464
|
type: fieldType,
|
|
426
465
|
isPrimary,
|
|
427
466
|
from: field,
|
|
467
|
+
unwrappedType: unwrapNestedType(field.type),
|
|
428
468
|
path: [...path.slice(1), key],
|
|
429
469
|
});
|
|
430
470
|
};
|
|
@@ -512,6 +552,7 @@ export const getSQLFields = (
|
|
|
512
552
|
type: "bool",
|
|
513
553
|
isPrimary: false,
|
|
514
554
|
from: undefined,
|
|
555
|
+
unwrappedType: undefined,
|
|
515
556
|
path: [...path.slice(1), key],
|
|
516
557
|
describesExistenceOfAnother: path[path.length - 1],
|
|
517
558
|
});
|
|
@@ -613,7 +654,7 @@ const getTableFromValue = (
|
|
|
613
654
|
field: Field,
|
|
614
655
|
value?: any,
|
|
615
656
|
): Table => {
|
|
616
|
-
let clazzName: string | undefined = undefined;
|
|
657
|
+
let clazzName: string | Constructor<any> | undefined = undefined;
|
|
617
658
|
if (!isNestedType(field.type)) {
|
|
618
659
|
clazzName = WRAPPED_SIMPLE_VALUE_VARIANT;
|
|
619
660
|
} else {
|
|
@@ -632,7 +673,7 @@ const getTableFromValue = (
|
|
|
632
673
|
continue;
|
|
633
674
|
}
|
|
634
675
|
if (ctor) {
|
|
635
|
-
clazzName =
|
|
676
|
+
clazzName = ctor;
|
|
636
677
|
break;
|
|
637
678
|
}
|
|
638
679
|
}
|
|
@@ -764,7 +805,7 @@ export const insert = async (
|
|
|
764
805
|
for (const _field of subTable.fields) {
|
|
765
806
|
bindableValues.push(null);
|
|
766
807
|
}
|
|
767
|
-
bindableValues[bindableValues.length - 1] =
|
|
808
|
+
bindableValues[bindableValues.length - 1] = 0; // assign the value "false" to the exist field column
|
|
768
809
|
continue;
|
|
769
810
|
}
|
|
770
811
|
|
|
@@ -773,7 +814,7 @@ export const insert = async (
|
|
|
773
814
|
if (table.inline) {
|
|
774
815
|
bindableValues.push(...values); // insert the bindable values into the parent bindable array
|
|
775
816
|
if (field.type instanceof OptionKind) {
|
|
776
|
-
bindableValues.push(
|
|
817
|
+
bindableValues.push(1); // assign the value "true" to the exist field column
|
|
777
818
|
}
|
|
778
819
|
return undefined;
|
|
779
820
|
} else {
|
|
@@ -889,7 +930,7 @@ export const generateSelectQuery = (
|
|
|
889
930
|
table: Table,
|
|
890
931
|
selects: { from: string; as: string }[],
|
|
891
932
|
) => {
|
|
892
|
-
return `
|
|
933
|
+
return `select ${selects.map((x) => `${x.from} as ${x.as}`).join(", ")} FROM ${table.name}`;
|
|
893
934
|
};
|
|
894
935
|
|
|
895
936
|
export const selectAllFieldsFromTables = (
|
|
@@ -901,24 +942,26 @@ export const selectAllFieldsFromTables = (
|
|
|
901
942
|
from: string;
|
|
902
943
|
as: string;
|
|
903
944
|
}[];
|
|
904
|
-
joins: Map<string,
|
|
945
|
+
joins: Map<string, JoinOrRootTable>;
|
|
946
|
+
groupBy: string | undefined;
|
|
905
947
|
}[] = [];
|
|
906
948
|
|
|
907
949
|
for (const table of tables) {
|
|
908
|
-
const {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
950
|
+
const {
|
|
951
|
+
selects,
|
|
952
|
+
join: joinFromSelect,
|
|
953
|
+
groupBy,
|
|
954
|
+
} = selectAllFieldsFromTable(table, shape);
|
|
955
|
+
|
|
956
|
+
selectsPerTable.push({ selects, joins: joinFromSelect, groupBy });
|
|
913
957
|
}
|
|
914
958
|
|
|
915
959
|
// pad with empty selects to make sure all selects have the same length
|
|
916
|
-
/* const maxSelects = Math.max(...selectsPerTable.map(x => x.selects.length)); */
|
|
917
|
-
|
|
918
960
|
let newSelects: {
|
|
919
961
|
from: string;
|
|
920
962
|
as: string;
|
|
921
963
|
}[][] = [];
|
|
964
|
+
|
|
922
965
|
for (const [i, selects] of selectsPerTable.entries()) {
|
|
923
966
|
const newSelect = [];
|
|
924
967
|
for (const [j, selectsOther] of selectsPerTable.entries()) {
|
|
@@ -931,11 +974,6 @@ export const selectAllFieldsFromTables = (
|
|
|
931
974
|
}
|
|
932
975
|
}
|
|
933
976
|
newSelects.push(newSelect);
|
|
934
|
-
|
|
935
|
-
/* let pad = 0;
|
|
936
|
-
while (select.selects.length < maxSelects) {
|
|
937
|
-
select.selects.push({ from: "NULL", as: `'pad#${++pad}'` });
|
|
938
|
-
} */
|
|
939
977
|
}
|
|
940
978
|
// also return table name
|
|
941
979
|
for (const [i, selects] of selectsPerTable.entries()) {
|
|
@@ -952,8 +990,67 @@ export const selectAllFieldsFromTable = (
|
|
|
952
990
|
let stack: { table: Table; shape?: types.Shape }[] = [{ table, shape }];
|
|
953
991
|
let join: Map<string, JoinTable> = new Map();
|
|
954
992
|
const fieldResolvers: { from: string; as: string }[] = [];
|
|
993
|
+
let groupByParentId = false;
|
|
955
994
|
for (const tableAndShape of stack) {
|
|
956
|
-
if (
|
|
995
|
+
if (tableAndShape.table.referencedInArray) {
|
|
996
|
+
let selectBuilder = `${JSON_GROUP_ARRAY}(${JSON_OBJECT}(`;
|
|
997
|
+
|
|
998
|
+
groupByParentId = true; // we need to group by the parent id as else we will not be returned with more than 1 result
|
|
999
|
+
|
|
1000
|
+
let first = false;
|
|
1001
|
+
const as = createReconstructReferenceName(tableAndShape.table);
|
|
1002
|
+
|
|
1003
|
+
for (const field of tableAndShape.table.fields) {
|
|
1004
|
+
if (
|
|
1005
|
+
(field.isPrimary ||
|
|
1006
|
+
!tableAndShape.shape ||
|
|
1007
|
+
matchFieldInShape(tableAndShape.shape, [], field) ||
|
|
1008
|
+
// also always include the index field
|
|
1009
|
+
field.name === ARRAY_INDEX_COLUMN) &&
|
|
1010
|
+
field.name !== PARENT_TABLE_ID
|
|
1011
|
+
) {
|
|
1012
|
+
let resolveField = `${as}.${escapeColumnName(field.name)}`;
|
|
1013
|
+
// if field is bigint we need to convert it to string, so that later in a JSON.parse scenario it is not converted to a number, but remains a string until we can convert it back to a bigint manually
|
|
1014
|
+
if (field.unwrappedType === "u64") {
|
|
1015
|
+
resolveField = `CAST(${resolveField} AS TEXT)`;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// if field is blob we need to convert it to hex string
|
|
1019
|
+
if (field.type === "BLOB") {
|
|
1020
|
+
resolveField = `HEX(${resolveField})`;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if (first) {
|
|
1024
|
+
selectBuilder += `, `;
|
|
1025
|
+
}
|
|
1026
|
+
first = true;
|
|
1027
|
+
selectBuilder += `${escapeColumnName(field.name, "'")}, ${resolveField}`;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
selectBuilder += `)) `; // FILTER (WHERE ${tableAndShape.table.name}.${tableAndShape.table.primary} IS NOT NULL)
|
|
1031
|
+
|
|
1032
|
+
fieldResolvers.push({
|
|
1033
|
+
from: selectBuilder,
|
|
1034
|
+
as,
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
join.set(createReconstructReferenceName(tableAndShape.table), {
|
|
1038
|
+
as,
|
|
1039
|
+
table: tableAndShape.table,
|
|
1040
|
+
type: "left" as const,
|
|
1041
|
+
columns: [],
|
|
1042
|
+
});
|
|
1043
|
+
} else if (!tableAndShape.table.inline) {
|
|
1044
|
+
// we end up here when we have simple joins we want to make that are not arrays, and not inlined
|
|
1045
|
+
if (tableAndShape.table.parent != null) {
|
|
1046
|
+
join.set(createReconstructReferenceName(tableAndShape.table), {
|
|
1047
|
+
as: tableAndShape.table.name,
|
|
1048
|
+
table: tableAndShape.table,
|
|
1049
|
+
type: "left" as const,
|
|
1050
|
+
columns: [],
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
|
|
957
1054
|
for (const field of tableAndShape.table.fields) {
|
|
958
1055
|
if (
|
|
959
1056
|
field.isPrimary ||
|
|
@@ -969,10 +1066,6 @@ export const selectAllFieldsFromTable = (
|
|
|
969
1066
|
}
|
|
970
1067
|
|
|
971
1068
|
for (const child of tableAndShape.table.children) {
|
|
972
|
-
if (child.referencedInArray) {
|
|
973
|
-
continue;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
1069
|
let childShape: types.Shape | undefined = undefined;
|
|
977
1070
|
if (tableAndShape.shape) {
|
|
978
1071
|
const parentPath = child.parentPath?.slice(1);
|
|
@@ -991,11 +1084,7 @@ export const selectAllFieldsFromTable = (
|
|
|
991
1084
|
? maybeShape[0]
|
|
992
1085
|
: maybeShape;
|
|
993
1086
|
}
|
|
994
|
-
|
|
995
1087
|
stack.push({ table: child, shape: childShape });
|
|
996
|
-
if (!child.inline) {
|
|
997
|
-
join.set(child.name, { as: child.name, table: child });
|
|
998
|
-
}
|
|
999
1088
|
}
|
|
1000
1089
|
}
|
|
1001
1090
|
|
|
@@ -1004,6 +1093,10 @@ export const selectAllFieldsFromTable = (
|
|
|
1004
1093
|
}
|
|
1005
1094
|
|
|
1006
1095
|
return {
|
|
1096
|
+
groupBy: groupByParentId
|
|
1097
|
+
? `${table.name}.${escapeColumnName(table.primary as string)}` ||
|
|
1098
|
+
undefined
|
|
1099
|
+
: undefined,
|
|
1007
1100
|
selects: fieldResolvers, // `SELECT ${fieldResolvers.join(", ")} FROM ${table.name}`,
|
|
1008
1101
|
join,
|
|
1009
1102
|
};
|
|
@@ -1062,24 +1155,55 @@ export const resolveInstanceFromValue = async <
|
|
|
1062
1155
|
: maybeShape;
|
|
1063
1156
|
|
|
1064
1157
|
if (isArray) {
|
|
1065
|
-
let once = false;
|
|
1158
|
+
/* let once = false; */
|
|
1066
1159
|
let resolvedArr = [];
|
|
1067
1160
|
|
|
1068
1161
|
for (const subtable of subTables) {
|
|
1069
|
-
//
|
|
1070
|
-
let
|
|
1071
|
-
const
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
subtable
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1162
|
+
// check if the array already in the provided row
|
|
1163
|
+
let arr: any[] | undefined = undefined;
|
|
1164
|
+
const tableName = createReconstructReferenceName(subtable);
|
|
1165
|
+
if (fromTablePrefixedValues[tableName]) {
|
|
1166
|
+
arr = JSON.parse(fromTablePrefixedValues[tableName]) as Array<any>;
|
|
1167
|
+
arr = arr.filter((x) => x[subtable.primary as string] != null);
|
|
1168
|
+
|
|
1169
|
+
// we need to go over all fields that are to be bigints and convert
|
|
1170
|
+
// them back to bigints
|
|
1171
|
+
// for blob fields we need to convert them back to Uint8Array
|
|
1172
|
+
for (const field of subtable.fields) {
|
|
1173
|
+
if (field.name === PARENT_TABLE_ID) {
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
if (field.unwrappedType === "u64") {
|
|
1177
|
+
for (const item of arr!) {
|
|
1178
|
+
item[field.name] = BigInt(item[field.name]);
|
|
1179
|
+
}
|
|
1180
|
+
} else if (field.type === "BLOB") {
|
|
1181
|
+
for (const item of arr!) {
|
|
1182
|
+
item[field.name] = fromHexString(item[field.name]);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
} else {
|
|
1187
|
+
if (subtable.children) {
|
|
1188
|
+
// TODO we only end up where when we resolve nested arrays,
|
|
1189
|
+
// which shoulld instead be resolved in a nested select (with json_group_array and json_object)
|
|
1190
|
+
let rootTable = getNonInlinedTable(table);
|
|
1191
|
+
const parentId =
|
|
1192
|
+
fromTablePrefixedValues[
|
|
1193
|
+
getTablePrefixedField(
|
|
1194
|
+
rootTable,
|
|
1195
|
+
rootTable.primary as string,
|
|
1196
|
+
!tablePrefixed,
|
|
1197
|
+
)
|
|
1198
|
+
];
|
|
1199
|
+
|
|
1200
|
+
arr = await resolveChildren(parentId, subtable);
|
|
1201
|
+
} else {
|
|
1202
|
+
arr = [];
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
if (arr && arr.length > 0) {
|
|
1206
|
+
/* once = true; */
|
|
1083
1207
|
for (const element of arr) {
|
|
1084
1208
|
const resolved: SimpleNested | any = await resolveInstanceFromValue(
|
|
1085
1209
|
element,
|
|
@@ -1097,11 +1221,7 @@ export const resolveInstanceFromValue = async <
|
|
|
1097
1221
|
}
|
|
1098
1222
|
}
|
|
1099
1223
|
|
|
1100
|
-
|
|
1101
|
-
obj[field.key] = undefined;
|
|
1102
|
-
} else {
|
|
1103
|
-
obj[field.key] = resolvedArr;
|
|
1104
|
-
}
|
|
1224
|
+
obj[field.key] = resolvedArr; // we can not do option(vec('T')) since we dont store the option type for Arrays (TODO)
|
|
1105
1225
|
} else {
|
|
1106
1226
|
// resolve nested object from row directly
|
|
1107
1227
|
/* let extracted: any = {} */
|
|
@@ -1234,7 +1354,7 @@ export const convertDeleteRequestToQuery = (
|
|
|
1234
1354
|
): { sql: string; bindable: any[] } => {
|
|
1235
1355
|
const { query, bindable } = convertRequestToQuery(
|
|
1236
1356
|
"delete",
|
|
1237
|
-
request,
|
|
1357
|
+
{ query: types.toQuery(request.query) },
|
|
1238
1358
|
tables,
|
|
1239
1359
|
table,
|
|
1240
1360
|
);
|
|
@@ -1251,7 +1371,7 @@ export const convertSumRequestToQuery = (
|
|
|
1251
1371
|
): { sql: string; bindable: any[] } => {
|
|
1252
1372
|
const { query, bindable } = convertRequestToQuery(
|
|
1253
1373
|
"sum",
|
|
1254
|
-
request,
|
|
1374
|
+
{ query: types.toQuery(request.query), key: request.key },
|
|
1255
1375
|
tables,
|
|
1256
1376
|
table,
|
|
1257
1377
|
);
|
|
@@ -1276,7 +1396,7 @@ export const convertCountRequestToQuery = (
|
|
|
1276
1396
|
): { sql: string; bindable: any[] } => {
|
|
1277
1397
|
const { query, bindable } = convertRequestToQuery(
|
|
1278
1398
|
"count",
|
|
1279
|
-
request,
|
|
1399
|
+
{ query: request?.query ? types.toQuery(request.query) : undefined },
|
|
1280
1400
|
tables,
|
|
1281
1401
|
table,
|
|
1282
1402
|
);
|
|
@@ -1286,13 +1406,76 @@ export const convertCountRequestToQuery = (
|
|
|
1286
1406
|
};
|
|
1287
1407
|
};
|
|
1288
1408
|
|
|
1409
|
+
const buildOrderBy = (
|
|
1410
|
+
sort: types.Sort[] | types.Sort | undefined,
|
|
1411
|
+
tables: Map<string, Table>,
|
|
1412
|
+
table: Table,
|
|
1413
|
+
joinBuilder: Map<string, JoinOrRootTable>,
|
|
1414
|
+
resolverBuilder: { from: string; as: string }[],
|
|
1415
|
+
path: string[] = [],
|
|
1416
|
+
options?: {
|
|
1417
|
+
fetchAll?: boolean;
|
|
1418
|
+
planner?: PlanningSession;
|
|
1419
|
+
},
|
|
1420
|
+
) => {
|
|
1421
|
+
let orderByBuilder: string | undefined = undefined;
|
|
1422
|
+
|
|
1423
|
+
if (
|
|
1424
|
+
(!sort || (Array.isArray(sort) && sort.length === 0)) &&
|
|
1425
|
+
!options?.fetchAll
|
|
1426
|
+
) {
|
|
1427
|
+
sort =
|
|
1428
|
+
table.primary && path.length === 0
|
|
1429
|
+
? [{ key: [table.primary], direction: types.SortDirection.ASC }]
|
|
1430
|
+
: undefined;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
if (sort) {
|
|
1434
|
+
let sortArr = Array.isArray(sort) ? sort : [sort];
|
|
1435
|
+
if (sortArr.length > 0) {
|
|
1436
|
+
orderByBuilder = "";
|
|
1437
|
+
let once = false;
|
|
1438
|
+
for (const sort of sortArr) {
|
|
1439
|
+
const { foreignTables, queryKey } = resolveTableToQuery(
|
|
1440
|
+
table,
|
|
1441
|
+
tables,
|
|
1442
|
+
joinBuilder,
|
|
1443
|
+
[...path, ...sort.key],
|
|
1444
|
+
undefined,
|
|
1445
|
+
true,
|
|
1446
|
+
);
|
|
1447
|
+
|
|
1448
|
+
for (const foreignTable of foreignTables) {
|
|
1449
|
+
if (once) {
|
|
1450
|
+
orderByBuilder += ", ";
|
|
1451
|
+
}
|
|
1452
|
+
once = true;
|
|
1453
|
+
|
|
1454
|
+
foreignTable.columns.push(queryKey); // add the sort key to the list of columns that will be used for this query
|
|
1455
|
+
orderByBuilder += `"${foreignTable.as}#${queryKey}" ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`;
|
|
1456
|
+
|
|
1457
|
+
resolverBuilder.push({
|
|
1458
|
+
from: `${table.name}.${escapeColumnName(queryKey)}`,
|
|
1459
|
+
as: `'${foreignTable.as}#${queryKey}'`,
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
return { orderByBuilder };
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1289
1469
|
export const convertSearchRequestToQuery = (
|
|
1290
|
-
request:
|
|
1470
|
+
request:
|
|
1471
|
+
| { query: types.Query[]; sort?: types.Sort[] | types.Sort }
|
|
1472
|
+
| undefined,
|
|
1291
1473
|
tables: Map<string, Table>,
|
|
1292
1474
|
rootTables: Table[],
|
|
1293
1475
|
options?: {
|
|
1294
1476
|
shape?: types.Shape | undefined;
|
|
1295
|
-
|
|
1477
|
+
fetchAll?: boolean;
|
|
1478
|
+
planner?: PlanningSession;
|
|
1296
1479
|
},
|
|
1297
1480
|
): { sql: string; bindable: any[] } => {
|
|
1298
1481
|
let unionBuilder = "";
|
|
@@ -1303,30 +1486,32 @@ export const convertSearchRequestToQuery = (
|
|
|
1303
1486
|
|
|
1304
1487
|
const selectsPerTable = selectAllFieldsFromTables(rootTables, options?.shape);
|
|
1305
1488
|
let bindableBuilder: any[] = [];
|
|
1489
|
+
|
|
1306
1490
|
for (const [i, table] of rootTables.entries()) {
|
|
1307
|
-
const { selects, joins
|
|
1308
|
-
|
|
1491
|
+
const { selects, joins, groupBy } = selectsPerTable[i];
|
|
1492
|
+
|
|
1309
1493
|
try {
|
|
1310
|
-
const {
|
|
1311
|
-
|
|
1312
|
-
request,
|
|
1494
|
+
const { orderByBuilder } = buildOrderBy(
|
|
1495
|
+
request?.sort,
|
|
1313
1496
|
tables,
|
|
1314
1497
|
table,
|
|
1315
|
-
|
|
1498
|
+
joins,
|
|
1499
|
+
selects,
|
|
1316
1500
|
[],
|
|
1317
|
-
|
|
1318
|
-
stable: options?.stable,
|
|
1319
|
-
},
|
|
1501
|
+
options,
|
|
1320
1502
|
);
|
|
1321
|
-
|
|
1322
|
-
orderByClause
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1503
|
+
|
|
1504
|
+
if (!orderByClause && orderByBuilder) {
|
|
1505
|
+
// assume all order by clauses will be the same
|
|
1506
|
+
orderByClause =
|
|
1507
|
+
orderByBuilder.length > 0
|
|
1508
|
+
? orderByClause.length > 0
|
|
1509
|
+
? orderByClause + ", " + orderByBuilder
|
|
1510
|
+
: orderByBuilder
|
|
1511
|
+
: orderByClause;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
//orderByAddedOnce = true;
|
|
1330
1515
|
} catch (error) {
|
|
1331
1516
|
if (error instanceof MissingFieldError) {
|
|
1332
1517
|
lastError = error;
|
|
@@ -1334,6 +1519,33 @@ export const convertSearchRequestToQuery = (
|
|
|
1334
1519
|
}
|
|
1335
1520
|
throw error;
|
|
1336
1521
|
}
|
|
1522
|
+
|
|
1523
|
+
const selectQuery = generateSelectQuery(table, selects);
|
|
1524
|
+
|
|
1525
|
+
for (const flattenRequest of flattenQuery(request)) {
|
|
1526
|
+
try {
|
|
1527
|
+
const { query, bindable } = convertRequestToQuery(
|
|
1528
|
+
"iterate",
|
|
1529
|
+
flattenRequest,
|
|
1530
|
+
tables,
|
|
1531
|
+
table,
|
|
1532
|
+
new Map(joins), // copy the map, else we might might do unececessary joins
|
|
1533
|
+
[],
|
|
1534
|
+
options,
|
|
1535
|
+
);
|
|
1536
|
+
|
|
1537
|
+
unionBuilder += `${unionBuilder.length > 0 ? " UNION " : ""} ${selectQuery} ${query} ${groupBy ? "GROUP BY " + groupBy : ""}`;
|
|
1538
|
+
matchedOnce = true;
|
|
1539
|
+
bindableBuilder.push(...bindable);
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
if (error instanceof MissingFieldError) {
|
|
1542
|
+
lastError = error;
|
|
1543
|
+
orderByClause = "";
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
throw error;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1337
1549
|
}
|
|
1338
1550
|
|
|
1339
1551
|
if (!matchedOnce) {
|
|
@@ -1341,20 +1553,43 @@ export const convertSearchRequestToQuery = (
|
|
|
1341
1553
|
}
|
|
1342
1554
|
|
|
1343
1555
|
return {
|
|
1344
|
-
sql: `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} limit ? offset
|
|
1556
|
+
sql: `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} ${options?.fetchAll ? "" : "limit ? offset ?"}`,
|
|
1345
1557
|
bindable: bindableBuilder,
|
|
1346
1558
|
};
|
|
1347
1559
|
};
|
|
1348
1560
|
|
|
1349
|
-
type SearchQueryParts = {
|
|
1350
|
-
|
|
1561
|
+
type SearchQueryParts = {
|
|
1562
|
+
query: string;
|
|
1563
|
+
/* orderBy: string; */
|
|
1564
|
+
bindable: any[];
|
|
1565
|
+
selects: string[];
|
|
1566
|
+
};
|
|
1567
|
+
type CountQueryParts = {
|
|
1568
|
+
query: string;
|
|
1569
|
+
join: string;
|
|
1570
|
+
bindable: any[];
|
|
1571
|
+
selects: string[];
|
|
1572
|
+
};
|
|
1351
1573
|
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
)
|
|
1356
|
-
|
|
1357
|
-
|
|
1574
|
+
const getOrSetRootTable = (
|
|
1575
|
+
joinBuilder: Map<string, JoinOrRootTable>,
|
|
1576
|
+
table: Table,
|
|
1577
|
+
) => {
|
|
1578
|
+
const refName = createQueryTableReferenceName(table);
|
|
1579
|
+
let ref = joinBuilder.get(refName);
|
|
1580
|
+
if (ref) {
|
|
1581
|
+
return ref;
|
|
1582
|
+
}
|
|
1583
|
+
const join = {
|
|
1584
|
+
// add the root as a join even though it is not, just so we can collect the columns it will be queried
|
|
1585
|
+
table: table,
|
|
1586
|
+
type: "root" as const,
|
|
1587
|
+
as: table.name,
|
|
1588
|
+
columns: [],
|
|
1589
|
+
};
|
|
1590
|
+
joinBuilder.set(refName, join);
|
|
1591
|
+
return join;
|
|
1592
|
+
};
|
|
1358
1593
|
|
|
1359
1594
|
const convertRequestToQuery = <
|
|
1360
1595
|
T extends "iterate" | "count" | "sum" | "delete",
|
|
@@ -1363,26 +1598,40 @@ const convertRequestToQuery = <
|
|
|
1363
1598
|
type: T,
|
|
1364
1599
|
request:
|
|
1365
1600
|
| (T extends "iterate"
|
|
1366
|
-
?
|
|
1601
|
+
? {
|
|
1602
|
+
query?: types.Query[];
|
|
1603
|
+
sort?: types.Sort[] | types.Sort;
|
|
1604
|
+
}
|
|
1367
1605
|
: T extends "count"
|
|
1368
|
-
?
|
|
1606
|
+
? {
|
|
1607
|
+
query?: types.Query[];
|
|
1608
|
+
}
|
|
1369
1609
|
: T extends "delete"
|
|
1370
|
-
?
|
|
1371
|
-
|
|
1610
|
+
? {
|
|
1611
|
+
query?: types.Query[];
|
|
1612
|
+
}
|
|
1613
|
+
: {
|
|
1614
|
+
query?: types.Query[];
|
|
1615
|
+
key: string | string[];
|
|
1616
|
+
})
|
|
1372
1617
|
| undefined,
|
|
1373
1618
|
tables: Map<string, Table>,
|
|
1374
1619
|
table: Table,
|
|
1375
|
-
extraJoin?: Map<string,
|
|
1620
|
+
extraJoin?: Map<string, JoinOrRootTable>,
|
|
1376
1621
|
path: string[] = [],
|
|
1377
1622
|
options?: {
|
|
1378
|
-
|
|
1623
|
+
fetchAll?: boolean;
|
|
1624
|
+
planner?: PlanningSession;
|
|
1379
1625
|
},
|
|
1380
1626
|
): R => {
|
|
1381
1627
|
let whereBuilder = "";
|
|
1382
1628
|
let bindableBuilder: any[] = [];
|
|
1383
|
-
let orderByBuilder: string | undefined = undefined;
|
|
1629
|
+
/* let orderByBuilder: string | undefined = undefined; */
|
|
1384
1630
|
/* let tablesToSelect: string[] = [table.name]; */
|
|
1385
|
-
let joinBuilder: Map<string,
|
|
1631
|
+
let joinBuilder: Map<string, JoinOrRootTable> = extraJoin || new Map();
|
|
1632
|
+
|
|
1633
|
+
getOrSetRootTable(joinBuilder, table);
|
|
1634
|
+
|
|
1386
1635
|
const coercedQuery = types.toQuery(request?.query);
|
|
1387
1636
|
if (coercedQuery.length === 1) {
|
|
1388
1637
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
@@ -1391,6 +1640,8 @@ const convertRequestToQuery = <
|
|
|
1391
1640
|
table,
|
|
1392
1641
|
joinBuilder,
|
|
1393
1642
|
path,
|
|
1643
|
+
undefined,
|
|
1644
|
+
0,
|
|
1394
1645
|
);
|
|
1395
1646
|
whereBuilder += where;
|
|
1396
1647
|
bindableBuilder.push(...bindable);
|
|
@@ -1401,14 +1652,19 @@ const convertRequestToQuery = <
|
|
|
1401
1652
|
table,
|
|
1402
1653
|
joinBuilder,
|
|
1403
1654
|
path,
|
|
1655
|
+
undefined,
|
|
1656
|
+
0,
|
|
1404
1657
|
);
|
|
1405
1658
|
whereBuilder += where;
|
|
1406
1659
|
bindableBuilder.push(...bindable);
|
|
1407
1660
|
}
|
|
1408
1661
|
|
|
1409
|
-
if (isIterateRequest(request, type)) {
|
|
1662
|
+
/* if (isIterateRequest(request, type)) {
|
|
1410
1663
|
let sort = request?.sort;
|
|
1411
|
-
if (
|
|
1664
|
+
if (
|
|
1665
|
+
(!sort || (Array.isArray(sort) && sort.length === 0)) &&
|
|
1666
|
+
!options?.fetchAll
|
|
1667
|
+
) {
|
|
1412
1668
|
sort =
|
|
1413
1669
|
table.primary && path.length === 0
|
|
1414
1670
|
? [{ key: [table.primary], direction: types.SortDirection.ASC }]
|
|
@@ -1429,61 +1685,97 @@ const convertRequestToQuery = <
|
|
|
1429
1685
|
undefined,
|
|
1430
1686
|
true,
|
|
1431
1687
|
);
|
|
1432
|
-
|
|
1688
|
+
|
|
1689
|
+
for (const foreignTable of foreignTables) {
|
|
1433
1690
|
if (once) {
|
|
1434
1691
|
orderByBuilder += ", ";
|
|
1435
1692
|
}
|
|
1436
1693
|
once = true;
|
|
1437
|
-
|
|
1694
|
+
|
|
1695
|
+
foreignTable.columns.push(queryKey); // add the sort key to the list of columns that will be used for this query
|
|
1696
|
+
|
|
1697
|
+
orderByBuilder += `${foreignTable.as}.${queryKey} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`;
|
|
1438
1698
|
}
|
|
1439
1699
|
}
|
|
1440
|
-
|
|
1441
|
-
/* orderByBuilder += request.sort
|
|
1442
|
-
.map(
|
|
1443
|
-
(sort) =>
|
|
1444
|
-
`${table.name}.${sort.key} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`
|
|
1445
|
-
)
|
|
1446
|
-
.join(", "); */
|
|
1447
1700
|
}
|
|
1448
1701
|
}
|
|
1449
|
-
}
|
|
1702
|
+
} */
|
|
1450
1703
|
const where = whereBuilder.length > 0 ? "where " + whereBuilder : undefined;
|
|
1451
1704
|
|
|
1452
1705
|
if (extraJoin && extraJoin.size > 0) {
|
|
1453
1706
|
insertMapIntoMap(joinBuilder, extraJoin);
|
|
1454
1707
|
}
|
|
1455
|
-
let join = buildJoin(joinBuilder,
|
|
1708
|
+
let { join } = buildJoin(joinBuilder, options);
|
|
1456
1709
|
|
|
1457
1710
|
const query = `${join ? join : ""} ${where ? where : ""}`;
|
|
1458
1711
|
|
|
1459
1712
|
return {
|
|
1460
1713
|
query,
|
|
1461
|
-
orderBy: orderByBuilder,
|
|
1714
|
+
/* orderBy: orderByBuilder, */
|
|
1462
1715
|
bindable: bindableBuilder,
|
|
1463
1716
|
} as R;
|
|
1464
1717
|
};
|
|
1465
1718
|
|
|
1466
1719
|
export const buildJoin = (
|
|
1467
|
-
joinBuilder: Map<string,
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1720
|
+
joinBuilder: Map<string, JoinOrRootTable>,
|
|
1721
|
+
options?: {
|
|
1722
|
+
planner?: PlanningSession;
|
|
1723
|
+
},
|
|
1724
|
+
): { join: string } => {
|
|
1725
|
+
/* let joinTypeDefault = resolveAllColumns
|
|
1726
|
+
? "CROSS JOIN"
|
|
1727
|
+
: "JOIN"; */
|
|
1473
1728
|
let join = "";
|
|
1729
|
+
|
|
1730
|
+
for (const [_key, table] of joinBuilder) {
|
|
1731
|
+
if (table.type !== "root") {
|
|
1732
|
+
continue;
|
|
1733
|
+
}
|
|
1734
|
+
const out = _buildJoin(table, options);
|
|
1735
|
+
join += out.join;
|
|
1736
|
+
}
|
|
1474
1737
|
for (const [_key, table] of joinBuilder) {
|
|
1738
|
+
if (table.type === "root") {
|
|
1739
|
+
continue;
|
|
1740
|
+
}
|
|
1741
|
+
const out = _buildJoin(table, options);
|
|
1742
|
+
join += out.join;
|
|
1743
|
+
}
|
|
1744
|
+
return { join };
|
|
1745
|
+
};
|
|
1746
|
+
|
|
1747
|
+
const _buildJoin = (
|
|
1748
|
+
table: JoinOrRootTable,
|
|
1749
|
+
options?: {
|
|
1750
|
+
planner?: PlanningSession;
|
|
1751
|
+
},
|
|
1752
|
+
) => {
|
|
1753
|
+
let join = "";
|
|
1754
|
+
let indexedBy: string | undefined = undefined;
|
|
1755
|
+
if (table.type !== "root") {
|
|
1756
|
+
table!.columns.push(PARENT_TABLE_ID); // we unshift because we join on the parent id before where clause
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
if (table!.columns.length > 0) {
|
|
1760
|
+
const usedColumns = removeDuplicatesOrdered(table!.columns);
|
|
1761
|
+
indexedBy = options?.planner
|
|
1762
|
+
? ` INDEXED BY ${options.planner.resolveIndex(table.table.name, usedColumns)} `
|
|
1763
|
+
: "";
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
if (table.type !== "root") {
|
|
1475
1767
|
let nonInlinedParent =
|
|
1476
1768
|
table.table.parent && getNonInlinedTable(table.table.parent);
|
|
1477
1769
|
if (!nonInlinedParent) {
|
|
1478
1770
|
throw new Error("Unexpected: missing parent");
|
|
1479
1771
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
join += `${joinType} ${table.table.name} AS ${table.as} ON ${nonInlinedParent.name}.${nonInlinedParent.primary} = ${table.as}.${PARENT_TABLE_ID} `;
|
|
1772
|
+
let joinType = table.type === "cross" ? "LEFT JOIN" : "LEFT JOIN";
|
|
1773
|
+
join += ` ${joinType} ${table.table.name} AS ${table.as} ${indexedBy} ON ${nonInlinedParent.name}.${nonInlinedParent.primary} = ${table.as}.${PARENT_TABLE_ID} `;
|
|
1774
|
+
} else if (indexedBy) {
|
|
1775
|
+
join += indexedBy;
|
|
1485
1776
|
}
|
|
1486
|
-
|
|
1777
|
+
|
|
1778
|
+
return { join };
|
|
1487
1779
|
};
|
|
1488
1780
|
|
|
1489
1781
|
const insertMapIntoMap = (map: Map<string, any>, insert: Map<string, any>) => {
|
|
@@ -1496,9 +1788,10 @@ export const convertQueryToSQLQuery = (
|
|
|
1496
1788
|
query: types.Query,
|
|
1497
1789
|
tables: Map<string, Table>,
|
|
1498
1790
|
table: Table,
|
|
1499
|
-
joinBuilder: Map<string,
|
|
1500
|
-
path: string[]
|
|
1501
|
-
tableAlias: string | undefined
|
|
1791
|
+
joinBuilder: Map<string, JoinOrRootTable>,
|
|
1792
|
+
path: string[],
|
|
1793
|
+
tableAlias: string | undefined,
|
|
1794
|
+
skipKeys: number,
|
|
1502
1795
|
): { where: string; bindable: any[] } => {
|
|
1503
1796
|
let whereBuilder = "";
|
|
1504
1797
|
let bindableBuilder: any[] = [];
|
|
@@ -1507,7 +1800,8 @@ export const convertQueryToSQLQuery = (
|
|
|
1507
1800
|
const handleAnd = (
|
|
1508
1801
|
queries: types.Query[],
|
|
1509
1802
|
path: string[],
|
|
1510
|
-
tableAlias
|
|
1803
|
+
tableAlias: string | undefined,
|
|
1804
|
+
keysOffset: number,
|
|
1511
1805
|
) => {
|
|
1512
1806
|
for (const query of queries) {
|
|
1513
1807
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
@@ -1517,6 +1811,7 @@ export const convertQueryToSQLQuery = (
|
|
|
1517
1811
|
joinBuilder,
|
|
1518
1812
|
path,
|
|
1519
1813
|
tableAlias,
|
|
1814
|
+
keysOffset,
|
|
1520
1815
|
);
|
|
1521
1816
|
whereBuilder =
|
|
1522
1817
|
whereBuilder.length > 0 ? `(${whereBuilder}) AND (${where})` : where;
|
|
@@ -1532,16 +1827,18 @@ export const convertQueryToSQLQuery = (
|
|
|
1532
1827
|
joinBuilder,
|
|
1533
1828
|
path,
|
|
1534
1829
|
tableAlias,
|
|
1830
|
+
skipKeys,
|
|
1535
1831
|
);
|
|
1536
1832
|
whereBuilder += where;
|
|
1537
1833
|
bindableBuilder.push(...bindable);
|
|
1538
1834
|
} else if (query instanceof types.Nested) {
|
|
1539
1835
|
let joinPrefix = "__" + String(tables.size);
|
|
1540
|
-
path = [...path, query.path];
|
|
1541
|
-
|
|
1836
|
+
path = [...path, ...query.path];
|
|
1837
|
+
let newSkipKeys = skipKeys + query.path.length;
|
|
1838
|
+
handleAnd(query.query, path, joinPrefix, newSkipKeys);
|
|
1542
1839
|
} else if (query instanceof types.LogicalQuery) {
|
|
1543
1840
|
if (query instanceof types.And) {
|
|
1544
|
-
handleAnd(query.and, path, tableAlias);
|
|
1841
|
+
handleAnd(query.and, path, tableAlias, skipKeys);
|
|
1545
1842
|
} else if (query instanceof types.Or) {
|
|
1546
1843
|
for (const subquery of query.or) {
|
|
1547
1844
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
@@ -1551,9 +1848,10 @@ export const convertQueryToSQLQuery = (
|
|
|
1551
1848
|
joinBuilder,
|
|
1552
1849
|
path,
|
|
1553
1850
|
tableAlias,
|
|
1851
|
+
skipKeys,
|
|
1554
1852
|
);
|
|
1555
1853
|
whereBuilder =
|
|
1556
|
-
whereBuilder.length > 0 ? `(${whereBuilder}) OR
|
|
1854
|
+
whereBuilder.length > 0 ? `(${whereBuilder}) OR(${where})` : where;
|
|
1557
1855
|
bindableBuilder.push(...bindable);
|
|
1558
1856
|
}
|
|
1559
1857
|
} else if (query instanceof types.Not) {
|
|
@@ -1564,8 +1862,9 @@ export const convertQueryToSQLQuery = (
|
|
|
1564
1862
|
joinBuilder,
|
|
1565
1863
|
path,
|
|
1566
1864
|
tableAlias,
|
|
1865
|
+
skipKeys,
|
|
1567
1866
|
);
|
|
1568
|
-
whereBuilder = `NOT
|
|
1867
|
+
whereBuilder = `NOT(${where})`;
|
|
1569
1868
|
bindableBuilder.push(...bindable);
|
|
1570
1869
|
} else {
|
|
1571
1870
|
throw new Error("Unsupported query type: " + query.constructor.name);
|
|
@@ -1584,38 +1883,54 @@ const cloneQuery = (query: types.StateFieldQuery) => {
|
|
|
1584
1883
|
return deserialize(serialize(query), types.StateFieldQuery);
|
|
1585
1884
|
};
|
|
1586
1885
|
|
|
1886
|
+
type JoinOrRootTable = JoinTable | RootTable;
|
|
1887
|
+
|
|
1587
1888
|
type JoinTable = {
|
|
1588
1889
|
table: Table;
|
|
1589
1890
|
as: string;
|
|
1891
|
+
type: "left" | "cross";
|
|
1892
|
+
columns: string[];
|
|
1893
|
+
};
|
|
1894
|
+
|
|
1895
|
+
type RootTable = {
|
|
1896
|
+
type: "root";
|
|
1897
|
+
table: Table;
|
|
1898
|
+
as: string;
|
|
1899
|
+
columns: string[];
|
|
1590
1900
|
};
|
|
1591
1901
|
|
|
1592
|
-
const
|
|
1902
|
+
/* const createQueryTableReferenceName = (
|
|
1593
1903
|
table: Table,
|
|
1594
1904
|
alias: string | undefined,
|
|
1595
|
-
fieldType: FieldType,
|
|
1596
|
-
joinSize: number,
|
|
1597
1905
|
) => {
|
|
1906
|
+
|
|
1598
1907
|
if (
|
|
1599
|
-
!alias
|
|
1600
|
-
(fieldType instanceof VecKind ||
|
|
1601
|
-
(fieldType instanceof OptionKind &&
|
|
1602
|
-
fieldType.elementType instanceof VecKind))
|
|
1908
|
+
!alias
|
|
1603
1909
|
) {
|
|
1604
|
-
let aliasSuffix =
|
|
1910
|
+
let aliasSuffix =
|
|
1911
|
+
"_query"; // "_" + String(joinSize); TODO this property will make every join unique, which is not wanted unless (ever?) since we can do OR in SQL which means we can do one join and perform AND/OR logic without joining multiple times to apply multiple conditions
|
|
1605
1912
|
alias = aliasSuffix;
|
|
1606
1913
|
}
|
|
1607
1914
|
const tableNameAs = alias ? alias + "_" + table.name : table.name;
|
|
1608
1915
|
return tableNameAs;
|
|
1916
|
+
}; */
|
|
1917
|
+
|
|
1918
|
+
const createQueryTableReferenceName = (table: Table) => {
|
|
1919
|
+
return table.parent == null ? table.name : "_query_" + table.name;
|
|
1920
|
+
};
|
|
1921
|
+
|
|
1922
|
+
const createReconstructReferenceName = (table: Table) => {
|
|
1923
|
+
return table.name; /* table.parent == null ? table.name : "_rec_" + table.name; */
|
|
1609
1924
|
};
|
|
1610
1925
|
|
|
1611
1926
|
const resolveTableToQuery = (
|
|
1612
1927
|
table: Table,
|
|
1613
1928
|
tables: Map<string, Table>,
|
|
1614
|
-
join: Map<string,
|
|
1929
|
+
join: Map<string, JoinOrRootTable>,
|
|
1615
1930
|
path: string[],
|
|
1616
1931
|
alias: string | undefined,
|
|
1617
1932
|
searchSelf: boolean,
|
|
1618
|
-
) => {
|
|
1933
|
+
): { queryKey: string; foreignTables: JoinOrRootTable[] } => {
|
|
1619
1934
|
// we are matching in two ways.
|
|
1620
1935
|
|
|
1621
1936
|
// 1. joins
|
|
@@ -1632,12 +1947,19 @@ const resolveTableToQuery = (
|
|
|
1632
1947
|
if (field) {
|
|
1633
1948
|
return {
|
|
1634
1949
|
queryKey: field.name,
|
|
1635
|
-
foreignTables: [
|
|
1950
|
+
foreignTables: [getOrSetRootTable(join, table)],
|
|
1636
1951
|
};
|
|
1637
1952
|
}
|
|
1638
1953
|
}
|
|
1639
1954
|
|
|
1640
|
-
let currentTables: JoinTable[] = [
|
|
1955
|
+
let currentTables: JoinTable[] = [
|
|
1956
|
+
{
|
|
1957
|
+
table,
|
|
1958
|
+
as: alias || table.name,
|
|
1959
|
+
type: "cross" as const,
|
|
1960
|
+
columns: [],
|
|
1961
|
+
},
|
|
1962
|
+
];
|
|
1641
1963
|
let prevTables: JoinTable[] | undefined = undefined;
|
|
1642
1964
|
|
|
1643
1965
|
// outer:
|
|
@@ -1650,20 +1972,29 @@ const resolveTableToQuery = (
|
|
|
1650
1972
|
if (!field && currentTable.children.length > 0) {
|
|
1651
1973
|
// second arg is needed because of polymorphic fields we might end up here intentially to check what tables to query
|
|
1652
1974
|
throw new MissingFieldError(
|
|
1653
|
-
`Property with key "${key}" is not found in the schema ${JSON.stringify(schema.fields.map((x) => x.key))}`,
|
|
1975
|
+
`Property with key "${key}" is not found in the schema ${JSON.stringify(schema.fields.map((x) => x.key))} `,
|
|
1654
1976
|
);
|
|
1655
1977
|
}
|
|
1656
1978
|
for (const child of currentTable.children) {
|
|
1657
|
-
const tableNameAs =
|
|
1979
|
+
const tableNameAs = createQueryTableReferenceName(
|
|
1658
1980
|
child,
|
|
1659
|
-
alias,
|
|
1981
|
+
/* alias */ /* ,
|
|
1660
1982
|
field.type,
|
|
1661
|
-
join.size,
|
|
1983
|
+
join.size, */
|
|
1662
1984
|
);
|
|
1985
|
+
|
|
1663
1986
|
let isMatching =
|
|
1664
1987
|
child.parentPath![child.parentPath!.length - 1] === key;
|
|
1665
1988
|
if (isMatching) {
|
|
1666
|
-
const tableWithAlias = {
|
|
1989
|
+
const tableWithAlias = {
|
|
1990
|
+
columns: [],
|
|
1991
|
+
table: child,
|
|
1992
|
+
as: tableNameAs,
|
|
1993
|
+
type:
|
|
1994
|
+
currentTable.children.length > 1
|
|
1995
|
+
? ("left" as const)
|
|
1996
|
+
: ("cross" as const),
|
|
1997
|
+
};
|
|
1667
1998
|
if (child.isSimpleValue) {
|
|
1668
1999
|
if (!child.inline) {
|
|
1669
2000
|
join.set(tableNameAs, tableWithAlias);
|
|
@@ -1727,9 +2058,10 @@ const convertStateFieldQuery = (
|
|
|
1727
2058
|
query: types.StateFieldQuery,
|
|
1728
2059
|
tables: Map<string, Table>,
|
|
1729
2060
|
table: Table,
|
|
1730
|
-
join: Map<string,
|
|
2061
|
+
join: Map<string, JoinOrRootTable>,
|
|
1731
2062
|
path: string[],
|
|
1732
|
-
tableAlias: string | undefined
|
|
2063
|
+
tableAlias: string | undefined,
|
|
2064
|
+
skipKeys: number,
|
|
1733
2065
|
): { where: string; bindable: any[] } => {
|
|
1734
2066
|
// if field id represented as foreign table, do join and compare
|
|
1735
2067
|
const inlinedName = getInlineTableFieldName(query.key);
|
|
@@ -1738,11 +2070,15 @@ const convertStateFieldQuery = (
|
|
|
1738
2070
|
); /* stringArraysEquals(query.key, [...table.parentPath, x.name]) )*/
|
|
1739
2071
|
const isForeign = !tableField; // table.fields.find(x => x.name === query.key[query.key.length - 1])
|
|
1740
2072
|
if (isForeign) {
|
|
2073
|
+
const tablePath: string[] = [...path];
|
|
2074
|
+
for (let i = skipKeys; i < query.key.length; i++) {
|
|
2075
|
+
tablePath.push(query.key[i]);
|
|
2076
|
+
}
|
|
1741
2077
|
const { queryKey, foreignTables } = resolveTableToQuery(
|
|
1742
2078
|
table,
|
|
1743
2079
|
tables,
|
|
1744
2080
|
join,
|
|
1745
|
-
|
|
2081
|
+
tablePath,
|
|
1746
2082
|
tableAlias,
|
|
1747
2083
|
false,
|
|
1748
2084
|
);
|
|
@@ -1755,6 +2091,7 @@ const convertStateFieldQuery = (
|
|
|
1755
2091
|
if (ftable.table === table) {
|
|
1756
2092
|
throw new Error("Unexpected");
|
|
1757
2093
|
}
|
|
2094
|
+
|
|
1758
2095
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
1759
2096
|
query,
|
|
1760
2097
|
tables,
|
|
@@ -1762,6 +2099,7 @@ const convertStateFieldQuery = (
|
|
|
1762
2099
|
join,
|
|
1763
2100
|
path,
|
|
1764
2101
|
ftable.as,
|
|
2102
|
+
skipKeys,
|
|
1765
2103
|
);
|
|
1766
2104
|
whereBuilder.push(where);
|
|
1767
2105
|
bindableBuilder.push(bindable);
|
|
@@ -1772,6 +2110,12 @@ const convertStateFieldQuery = (
|
|
|
1772
2110
|
};
|
|
1773
2111
|
}
|
|
1774
2112
|
|
|
2113
|
+
const columnAggregator = join.get(createQueryTableReferenceName(table))!;
|
|
2114
|
+
if (!columnAggregator) {
|
|
2115
|
+
throw new Error("Unexpected");
|
|
2116
|
+
}
|
|
2117
|
+
columnAggregator.columns.push(inlinedName);
|
|
2118
|
+
|
|
1775
2119
|
let bindable: any[] = [];
|
|
1776
2120
|
const keyWithTable =
|
|
1777
2121
|
(tableAlias || table.name) + "." + escapeColumnName(inlinedName);
|
|
@@ -1780,10 +2124,10 @@ const convertStateFieldQuery = (
|
|
|
1780
2124
|
let statement = "";
|
|
1781
2125
|
|
|
1782
2126
|
if (query.method === types.StringMatchMethod.contains) {
|
|
1783
|
-
statement = `${keyWithTable} LIKE
|
|
2127
|
+
statement = `${keyWithTable} LIKE ? `;
|
|
1784
2128
|
bindable.push(`%${query.value}%`);
|
|
1785
2129
|
} else if (query.method === types.StringMatchMethod.prefix) {
|
|
1786
|
-
statement = `${keyWithTable} LIKE
|
|
2130
|
+
statement = `${keyWithTable} LIKE ? `;
|
|
1787
2131
|
bindable.push(`${query.value}%`);
|
|
1788
2132
|
} else if (query.method === types.StringMatchMethod.exact) {
|
|
1789
2133
|
statement = `${keyWithTable} = ?`;
|
|
@@ -1802,7 +2146,7 @@ const convertStateFieldQuery = (
|
|
|
1802
2146
|
} else if (query instanceof types.IntegerCompare) {
|
|
1803
2147
|
if (tableField!.type === "BLOB") {
|
|
1804
2148
|
// TODO perf
|
|
1805
|
-
where = `hex(${keyWithTable}) LIKE
|
|
2149
|
+
where = `hex(${keyWithTable}) LIKE ? `;
|
|
1806
2150
|
bindable.push(
|
|
1807
2151
|
`%${toHexString(new Uint8Array([Number(query.value.value)]))}%`,
|
|
1808
2152
|
);
|
|
@@ -1810,20 +2154,20 @@ const convertStateFieldQuery = (
|
|
|
1810
2154
|
if (query.compare === types.Compare.Equal) {
|
|
1811
2155
|
where = `${keyWithTable} = ?`;
|
|
1812
2156
|
} else if (query.compare === types.Compare.Greater) {
|
|
1813
|
-
where = `${keyWithTable} >
|
|
2157
|
+
where = `${keyWithTable} > ? `;
|
|
1814
2158
|
} else if (query.compare === types.Compare.Less) {
|
|
1815
|
-
where = `${keyWithTable}
|
|
2159
|
+
where = `${keyWithTable} <?`;
|
|
1816
2160
|
} else if (query.compare === types.Compare.GreaterOrEqual) {
|
|
1817
|
-
where = `${keyWithTable} >=
|
|
2161
|
+
where = `${keyWithTable} >= ? `;
|
|
1818
2162
|
} else if (query.compare === types.Compare.LessOrEqual) {
|
|
1819
|
-
where = `${keyWithTable} <=
|
|
2163
|
+
where = `${keyWithTable} <= ? `;
|
|
1820
2164
|
} else {
|
|
1821
|
-
throw new Error(`Unsupported compare type: ${query.compare}`);
|
|
2165
|
+
throw new Error(`Unsupported compare type: ${query.compare} `);
|
|
1822
2166
|
}
|
|
1823
2167
|
|
|
1824
2168
|
if (unwrapNestedType(tableField.from!.type) === "u64") {
|
|
1825
2169
|
// shift left because that is how we insert the value
|
|
1826
|
-
bindable.push(query.value.value);
|
|
2170
|
+
bindable.push(u64ToI64(query.value.value));
|
|
1827
2171
|
} else {
|
|
1828
2172
|
bindable.push(query.value.value);
|
|
1829
2173
|
}
|
|
@@ -1838,3 +2182,14 @@ const convertStateFieldQuery = (
|
|
|
1838
2182
|
}
|
|
1839
2183
|
return { where, bindable };
|
|
1840
2184
|
};
|
|
2185
|
+
|
|
2186
|
+
const removeDuplicatesOrdered = (arr: string[]) => {
|
|
2187
|
+
let seen = new Set();
|
|
2188
|
+
return arr.filter((item) => {
|
|
2189
|
+
if (seen.has(item)) {
|
|
2190
|
+
return false;
|
|
2191
|
+
}
|
|
2192
|
+
seen.add(item);
|
|
2193
|
+
return true;
|
|
2194
|
+
});
|
|
2195
|
+
};
|