@peerbit/indexer-sqlite3 1.1.3 → 1.1.4-5cf61cb
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 +651 -163
- 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 +29 -7
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +354 -119
- 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 +78 -78
- package/src/engine.ts +143 -68
- package/src/query-planner.ts +334 -0
- package/src/schema.ts +498 -160
- 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,9 @@ 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
|
+
|
|
57
61
|
export const u64ToI64 = (u64: bigint | number) => {
|
|
58
62
|
return (typeof u64 === "number" ? BigInt(u64) : u64) - 9223372036854775808n;
|
|
59
63
|
};
|
|
@@ -80,7 +84,8 @@ export const convertToSQLType = (
|
|
|
80
84
|
};
|
|
81
85
|
|
|
82
86
|
const nullAsUndefined = (value: any) => (value === null ? undefined : value);
|
|
83
|
-
export const escapeColumnName = (name: string) =>
|
|
87
|
+
export const escapeColumnName = (name: string, char = '"') =>
|
|
88
|
+
`${char}${name}${char}`;
|
|
84
89
|
|
|
85
90
|
export class MissingFieldError extends Error {
|
|
86
91
|
constructor(message: string) {
|
|
@@ -152,6 +157,7 @@ type SQLField = {
|
|
|
152
157
|
type: string;
|
|
153
158
|
isPrimary: boolean;
|
|
154
159
|
from: Field | undefined;
|
|
160
|
+
unwrappedType: FieldType | undefined;
|
|
155
161
|
path: string[];
|
|
156
162
|
describesExistenceOfAnother?: string;
|
|
157
163
|
};
|
|
@@ -172,6 +178,7 @@ export interface Table {
|
|
|
172
178
|
parent: Table | undefined;
|
|
173
179
|
referencedInArray: boolean;
|
|
174
180
|
isSimpleValue: boolean;
|
|
181
|
+
indices: Set<string>;
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
export const getSQLTable = (
|
|
@@ -220,6 +227,7 @@ export const getSQLTable = (
|
|
|
220
227
|
referencedInArray: false,
|
|
221
228
|
isSimpleValue: false,
|
|
222
229
|
inline,
|
|
230
|
+
indices: new Set<string>(),
|
|
223
231
|
};
|
|
224
232
|
ret.push(table);
|
|
225
233
|
for (const dep of dependencies) {
|
|
@@ -259,6 +267,14 @@ export const getTableName = (
|
|
|
259
267
|
path: string[] = [],
|
|
260
268
|
clazz: string | Constructor<any>,
|
|
261
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
|
+
|
|
262
278
|
let name: string = typeof clazz === "string" ? clazz : getNameOfClass(clazz);
|
|
263
279
|
|
|
264
280
|
// prefix the generated table name so that the name is a valid SQL identifier (table name)
|
|
@@ -266,9 +282,11 @@ export const getTableName = (
|
|
|
266
282
|
|
|
267
283
|
// leading _ to allow path to have numbers
|
|
268
284
|
|
|
269
|
-
const ret =
|
|
270
|
-
|
|
271
|
-
|
|
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
|
+
}
|
|
272
290
|
return ret;
|
|
273
291
|
};
|
|
274
292
|
|
|
@@ -318,13 +336,14 @@ export const getSQLFields = (
|
|
|
318
336
|
? addJoinFieldFromParent
|
|
319
337
|
: (fields: SQLField[], contstraints: SQLConstraint[]) => {
|
|
320
338
|
// we resolve primary field here since it might be unknown until this point
|
|
321
|
-
const
|
|
339
|
+
const parentPrimaryField =
|
|
322
340
|
primary != null
|
|
323
341
|
? sqlFields.find((field) => field.name === primary)
|
|
324
342
|
: undefined;
|
|
325
|
-
const parentPrimaryFieldName =
|
|
326
|
-
|
|
327
|
-
|
|
343
|
+
const parentPrimaryFieldName =
|
|
344
|
+
parentPrimaryField?.key || CHILD_TABLE_ID;
|
|
345
|
+
const parentPrimaryFieldType = parentPrimaryField
|
|
346
|
+
? parentPrimaryField.type
|
|
328
347
|
: "INTEGER";
|
|
329
348
|
|
|
330
349
|
fields.unshift(
|
|
@@ -335,6 +354,7 @@ export const getSQLFields = (
|
|
|
335
354
|
type: "INTEGER",
|
|
336
355
|
isPrimary: true,
|
|
337
356
|
from: undefined,
|
|
357
|
+
unwrappedType: undefined,
|
|
338
358
|
path: [CHILD_TABLE_ID],
|
|
339
359
|
},
|
|
340
360
|
|
|
@@ -344,8 +364,9 @@ export const getSQLFields = (
|
|
|
344
364
|
key: PARENT_TABLE_ID,
|
|
345
365
|
definition: `${PARENT_TABLE_ID} ${parentPrimaryFieldType}`,
|
|
346
366
|
type: parentPrimaryFieldType,
|
|
367
|
+
from: parentPrimaryField?.from,
|
|
368
|
+
unwrappedType: parentPrimaryField?.unwrappedType,
|
|
347
369
|
isPrimary: false,
|
|
348
|
-
from: undefined,
|
|
349
370
|
path: [PARENT_TABLE_ID],
|
|
350
371
|
},
|
|
351
372
|
);
|
|
@@ -412,6 +433,7 @@ export const getSQLFields = (
|
|
|
412
433
|
type: "INTEGER",
|
|
413
434
|
isPrimary: false,
|
|
414
435
|
from: undefined,
|
|
436
|
+
unwrappedType: undefined,
|
|
415
437
|
path: [ARRAY_INDEX_COLUMN],
|
|
416
438
|
},
|
|
417
439
|
...table.fields.slice(2),
|
|
@@ -442,6 +464,7 @@ export const getSQLFields = (
|
|
|
442
464
|
type: fieldType,
|
|
443
465
|
isPrimary,
|
|
444
466
|
from: field,
|
|
467
|
+
unwrappedType: unwrapNestedType(field.type),
|
|
445
468
|
path: [...path.slice(1), key],
|
|
446
469
|
});
|
|
447
470
|
};
|
|
@@ -529,6 +552,7 @@ export const getSQLFields = (
|
|
|
529
552
|
type: "bool",
|
|
530
553
|
isPrimary: false,
|
|
531
554
|
from: undefined,
|
|
555
|
+
unwrappedType: undefined,
|
|
532
556
|
path: [...path.slice(1), key],
|
|
533
557
|
describesExistenceOfAnother: path[path.length - 1],
|
|
534
558
|
});
|
|
@@ -630,7 +654,7 @@ const getTableFromValue = (
|
|
|
630
654
|
field: Field,
|
|
631
655
|
value?: any,
|
|
632
656
|
): Table => {
|
|
633
|
-
let clazzName: string | undefined = undefined;
|
|
657
|
+
let clazzName: string | Constructor<any> | undefined = undefined;
|
|
634
658
|
if (!isNestedType(field.type)) {
|
|
635
659
|
clazzName = WRAPPED_SIMPLE_VALUE_VARIANT;
|
|
636
660
|
} else {
|
|
@@ -649,7 +673,7 @@ const getTableFromValue = (
|
|
|
649
673
|
continue;
|
|
650
674
|
}
|
|
651
675
|
if (ctor) {
|
|
652
|
-
clazzName =
|
|
676
|
+
clazzName = ctor;
|
|
653
677
|
break;
|
|
654
678
|
}
|
|
655
679
|
}
|
|
@@ -781,7 +805,7 @@ export const insert = async (
|
|
|
781
805
|
for (const _field of subTable.fields) {
|
|
782
806
|
bindableValues.push(null);
|
|
783
807
|
}
|
|
784
|
-
bindableValues[bindableValues.length - 1] =
|
|
808
|
+
bindableValues[bindableValues.length - 1] = 0; // assign the value "false" to the exist field column
|
|
785
809
|
continue;
|
|
786
810
|
}
|
|
787
811
|
|
|
@@ -790,7 +814,7 @@ export const insert = async (
|
|
|
790
814
|
if (table.inline) {
|
|
791
815
|
bindableValues.push(...values); // insert the bindable values into the parent bindable array
|
|
792
816
|
if (field.type instanceof OptionKind) {
|
|
793
|
-
bindableValues.push(
|
|
817
|
+
bindableValues.push(1); // assign the value "true" to the exist field column
|
|
794
818
|
}
|
|
795
819
|
return undefined;
|
|
796
820
|
} else {
|
|
@@ -906,7 +930,7 @@ export const generateSelectQuery = (
|
|
|
906
930
|
table: Table,
|
|
907
931
|
selects: { from: string; as: string }[],
|
|
908
932
|
) => {
|
|
909
|
-
return `
|
|
933
|
+
return `select ${selects.map((x) => `${x.from} as ${x.as}`).join(", ")} FROM ${table.name}`;
|
|
910
934
|
};
|
|
911
935
|
|
|
912
936
|
export const selectAllFieldsFromTables = (
|
|
@@ -918,24 +942,26 @@ export const selectAllFieldsFromTables = (
|
|
|
918
942
|
from: string;
|
|
919
943
|
as: string;
|
|
920
944
|
}[];
|
|
921
|
-
joins: Map<string,
|
|
945
|
+
joins: Map<string, JoinOrRootTable>;
|
|
946
|
+
groupBy: string | undefined;
|
|
922
947
|
}[] = [];
|
|
923
948
|
|
|
924
949
|
for (const table of tables) {
|
|
925
|
-
const {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
950
|
+
const {
|
|
951
|
+
selects,
|
|
952
|
+
join: joinFromSelect,
|
|
953
|
+
groupBy,
|
|
954
|
+
} = selectAllFieldsFromTable(table, shape);
|
|
955
|
+
|
|
956
|
+
selectsPerTable.push({ selects, joins: joinFromSelect, groupBy });
|
|
930
957
|
}
|
|
931
958
|
|
|
932
959
|
// pad with empty selects to make sure all selects have the same length
|
|
933
|
-
/* const maxSelects = Math.max(...selectsPerTable.map(x => x.selects.length)); */
|
|
934
|
-
|
|
935
960
|
let newSelects: {
|
|
936
961
|
from: string;
|
|
937
962
|
as: string;
|
|
938
963
|
}[][] = [];
|
|
964
|
+
|
|
939
965
|
for (const [i, selects] of selectsPerTable.entries()) {
|
|
940
966
|
const newSelect = [];
|
|
941
967
|
for (const [j, selectsOther] of selectsPerTable.entries()) {
|
|
@@ -948,11 +974,6 @@ export const selectAllFieldsFromTables = (
|
|
|
948
974
|
}
|
|
949
975
|
}
|
|
950
976
|
newSelects.push(newSelect);
|
|
951
|
-
|
|
952
|
-
/* let pad = 0;
|
|
953
|
-
while (select.selects.length < maxSelects) {
|
|
954
|
-
select.selects.push({ from: "NULL", as: `'pad#${++pad}'` });
|
|
955
|
-
} */
|
|
956
977
|
}
|
|
957
978
|
// also return table name
|
|
958
979
|
for (const [i, selects] of selectsPerTable.entries()) {
|
|
@@ -969,8 +990,67 @@ export const selectAllFieldsFromTable = (
|
|
|
969
990
|
let stack: { table: Table; shape?: types.Shape }[] = [{ table, shape }];
|
|
970
991
|
let join: Map<string, JoinTable> = new Map();
|
|
971
992
|
const fieldResolvers: { from: string; as: string }[] = [];
|
|
993
|
+
let groupByParentId = false;
|
|
972
994
|
for (const tableAndShape of stack) {
|
|
973
|
-
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
|
+
|
|
974
1054
|
for (const field of tableAndShape.table.fields) {
|
|
975
1055
|
if (
|
|
976
1056
|
field.isPrimary ||
|
|
@@ -986,10 +1066,6 @@ export const selectAllFieldsFromTable = (
|
|
|
986
1066
|
}
|
|
987
1067
|
|
|
988
1068
|
for (const child of tableAndShape.table.children) {
|
|
989
|
-
if (child.referencedInArray) {
|
|
990
|
-
continue;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
1069
|
let childShape: types.Shape | undefined = undefined;
|
|
994
1070
|
if (tableAndShape.shape) {
|
|
995
1071
|
const parentPath = child.parentPath?.slice(1);
|
|
@@ -1008,11 +1084,7 @@ export const selectAllFieldsFromTable = (
|
|
|
1008
1084
|
? maybeShape[0]
|
|
1009
1085
|
: maybeShape;
|
|
1010
1086
|
}
|
|
1011
|
-
|
|
1012
1087
|
stack.push({ table: child, shape: childShape });
|
|
1013
|
-
if (!child.inline) {
|
|
1014
|
-
join.set(child.name, { as: child.name, table: child });
|
|
1015
|
-
}
|
|
1016
1088
|
}
|
|
1017
1089
|
}
|
|
1018
1090
|
|
|
@@ -1021,6 +1093,10 @@ export const selectAllFieldsFromTable = (
|
|
|
1021
1093
|
}
|
|
1022
1094
|
|
|
1023
1095
|
return {
|
|
1096
|
+
groupBy: groupByParentId
|
|
1097
|
+
? `${table.name}.${escapeColumnName(table.primary as string)}` ||
|
|
1098
|
+
undefined
|
|
1099
|
+
: undefined,
|
|
1024
1100
|
selects: fieldResolvers, // `SELECT ${fieldResolvers.join(", ")} FROM ${table.name}`,
|
|
1025
1101
|
join,
|
|
1026
1102
|
};
|
|
@@ -1079,24 +1155,55 @@ export const resolveInstanceFromValue = async <
|
|
|
1079
1155
|
: maybeShape;
|
|
1080
1156
|
|
|
1081
1157
|
if (isArray) {
|
|
1082
|
-
let once = false;
|
|
1158
|
+
/* let once = false; */
|
|
1083
1159
|
let resolvedArr = [];
|
|
1084
1160
|
|
|
1085
1161
|
for (const subtable of subTables) {
|
|
1086
|
-
//
|
|
1087
|
-
let
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
subtable
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
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; */
|
|
1100
1207
|
for (const element of arr) {
|
|
1101
1208
|
const resolved: SimpleNested | any = await resolveInstanceFromValue(
|
|
1102
1209
|
element,
|
|
@@ -1114,11 +1221,7 @@ export const resolveInstanceFromValue = async <
|
|
|
1114
1221
|
}
|
|
1115
1222
|
}
|
|
1116
1223
|
|
|
1117
|
-
|
|
1118
|
-
obj[field.key] = undefined;
|
|
1119
|
-
} else {
|
|
1120
|
-
obj[field.key] = resolvedArr;
|
|
1121
|
-
}
|
|
1224
|
+
obj[field.key] = resolvedArr; // we can not do option(vec('T')) since we dont store the option type for Arrays (TODO)
|
|
1122
1225
|
} else {
|
|
1123
1226
|
// resolve nested object from row directly
|
|
1124
1227
|
/* let extracted: any = {} */
|
|
@@ -1251,7 +1354,7 @@ export const convertDeleteRequestToQuery = (
|
|
|
1251
1354
|
): { sql: string; bindable: any[] } => {
|
|
1252
1355
|
const { query, bindable } = convertRequestToQuery(
|
|
1253
1356
|
"delete",
|
|
1254
|
-
request,
|
|
1357
|
+
{ query: types.toQuery(request.query) },
|
|
1255
1358
|
tables,
|
|
1256
1359
|
table,
|
|
1257
1360
|
);
|
|
@@ -1268,7 +1371,7 @@ export const convertSumRequestToQuery = (
|
|
|
1268
1371
|
): { sql: string; bindable: any[] } => {
|
|
1269
1372
|
const { query, bindable } = convertRequestToQuery(
|
|
1270
1373
|
"sum",
|
|
1271
|
-
request,
|
|
1374
|
+
{ query: types.toQuery(request.query), key: request.key },
|
|
1272
1375
|
tables,
|
|
1273
1376
|
table,
|
|
1274
1377
|
);
|
|
@@ -1293,7 +1396,7 @@ export const convertCountRequestToQuery = (
|
|
|
1293
1396
|
): { sql: string; bindable: any[] } => {
|
|
1294
1397
|
const { query, bindable } = convertRequestToQuery(
|
|
1295
1398
|
"count",
|
|
1296
|
-
request,
|
|
1399
|
+
{ query: request?.query ? types.toQuery(request.query) : undefined },
|
|
1297
1400
|
tables,
|
|
1298
1401
|
table,
|
|
1299
1402
|
);
|
|
@@ -1303,13 +1406,76 @@ export const convertCountRequestToQuery = (
|
|
|
1303
1406
|
};
|
|
1304
1407
|
};
|
|
1305
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
|
+
|
|
1306
1469
|
export const convertSearchRequestToQuery = (
|
|
1307
|
-
request:
|
|
1470
|
+
request:
|
|
1471
|
+
| { query: types.Query[]; sort?: types.Sort[] | types.Sort }
|
|
1472
|
+
| undefined,
|
|
1308
1473
|
tables: Map<string, Table>,
|
|
1309
1474
|
rootTables: Table[],
|
|
1310
1475
|
options?: {
|
|
1311
1476
|
shape?: types.Shape | undefined;
|
|
1312
|
-
|
|
1477
|
+
fetchAll?: boolean;
|
|
1478
|
+
planner?: PlanningSession;
|
|
1313
1479
|
},
|
|
1314
1480
|
): { sql: string; bindable: any[] } => {
|
|
1315
1481
|
let unionBuilder = "";
|
|
@@ -1320,30 +1486,32 @@ export const convertSearchRequestToQuery = (
|
|
|
1320
1486
|
|
|
1321
1487
|
const selectsPerTable = selectAllFieldsFromTables(rootTables, options?.shape);
|
|
1322
1488
|
let bindableBuilder: any[] = [];
|
|
1489
|
+
|
|
1323
1490
|
for (const [i, table] of rootTables.entries()) {
|
|
1324
|
-
const { selects, joins
|
|
1325
|
-
|
|
1491
|
+
const { selects, joins, groupBy } = selectsPerTable[i];
|
|
1492
|
+
|
|
1326
1493
|
try {
|
|
1327
|
-
const {
|
|
1328
|
-
|
|
1329
|
-
request,
|
|
1494
|
+
const { orderByBuilder } = buildOrderBy(
|
|
1495
|
+
request?.sort,
|
|
1330
1496
|
tables,
|
|
1331
1497
|
table,
|
|
1332
|
-
|
|
1498
|
+
joins,
|
|
1499
|
+
selects,
|
|
1333
1500
|
[],
|
|
1334
|
-
|
|
1335
|
-
stable: options?.stable,
|
|
1336
|
-
},
|
|
1501
|
+
options,
|
|
1337
1502
|
);
|
|
1338
|
-
|
|
1339
|
-
orderByClause
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
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;
|
|
1347
1515
|
} catch (error) {
|
|
1348
1516
|
if (error instanceof MissingFieldError) {
|
|
1349
1517
|
lastError = error;
|
|
@@ -1351,6 +1519,33 @@ export const convertSearchRequestToQuery = (
|
|
|
1351
1519
|
}
|
|
1352
1520
|
throw error;
|
|
1353
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
|
+
}
|
|
1354
1549
|
}
|
|
1355
1550
|
|
|
1356
1551
|
if (!matchedOnce) {
|
|
@@ -1358,20 +1553,43 @@ export const convertSearchRequestToQuery = (
|
|
|
1358
1553
|
}
|
|
1359
1554
|
|
|
1360
1555
|
return {
|
|
1361
|
-
sql: `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} limit ? offset
|
|
1556
|
+
sql: `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} ${options?.fetchAll ? "" : "limit ? offset ?"}`,
|
|
1362
1557
|
bindable: bindableBuilder,
|
|
1363
1558
|
};
|
|
1364
1559
|
};
|
|
1365
1560
|
|
|
1366
|
-
type SearchQueryParts = {
|
|
1367
|
-
|
|
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
|
+
};
|
|
1368
1573
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
)
|
|
1373
|
-
|
|
1374
|
-
|
|
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
|
+
};
|
|
1375
1593
|
|
|
1376
1594
|
const convertRequestToQuery = <
|
|
1377
1595
|
T extends "iterate" | "count" | "sum" | "delete",
|
|
@@ -1380,26 +1598,40 @@ const convertRequestToQuery = <
|
|
|
1380
1598
|
type: T,
|
|
1381
1599
|
request:
|
|
1382
1600
|
| (T extends "iterate"
|
|
1383
|
-
?
|
|
1601
|
+
? {
|
|
1602
|
+
query?: types.Query[];
|
|
1603
|
+
sort?: types.Sort[] | types.Sort;
|
|
1604
|
+
}
|
|
1384
1605
|
: T extends "count"
|
|
1385
|
-
?
|
|
1606
|
+
? {
|
|
1607
|
+
query?: types.Query[];
|
|
1608
|
+
}
|
|
1386
1609
|
: T extends "delete"
|
|
1387
|
-
?
|
|
1388
|
-
|
|
1610
|
+
? {
|
|
1611
|
+
query?: types.Query[];
|
|
1612
|
+
}
|
|
1613
|
+
: {
|
|
1614
|
+
query?: types.Query[];
|
|
1615
|
+
key: string | string[];
|
|
1616
|
+
})
|
|
1389
1617
|
| undefined,
|
|
1390
1618
|
tables: Map<string, Table>,
|
|
1391
1619
|
table: Table,
|
|
1392
|
-
extraJoin?: Map<string,
|
|
1620
|
+
extraJoin?: Map<string, JoinOrRootTable>,
|
|
1393
1621
|
path: string[] = [],
|
|
1394
1622
|
options?: {
|
|
1395
|
-
|
|
1623
|
+
fetchAll?: boolean;
|
|
1624
|
+
planner?: PlanningSession;
|
|
1396
1625
|
},
|
|
1397
1626
|
): R => {
|
|
1398
1627
|
let whereBuilder = "";
|
|
1399
1628
|
let bindableBuilder: any[] = [];
|
|
1400
|
-
let orderByBuilder: string | undefined = undefined;
|
|
1629
|
+
/* let orderByBuilder: string | undefined = undefined; */
|
|
1401
1630
|
/* let tablesToSelect: string[] = [table.name]; */
|
|
1402
|
-
let joinBuilder: Map<string,
|
|
1631
|
+
let joinBuilder: Map<string, JoinOrRootTable> = extraJoin || new Map();
|
|
1632
|
+
|
|
1633
|
+
getOrSetRootTable(joinBuilder, table);
|
|
1634
|
+
|
|
1403
1635
|
const coercedQuery = types.toQuery(request?.query);
|
|
1404
1636
|
if (coercedQuery.length === 1) {
|
|
1405
1637
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
@@ -1408,6 +1640,8 @@ const convertRequestToQuery = <
|
|
|
1408
1640
|
table,
|
|
1409
1641
|
joinBuilder,
|
|
1410
1642
|
path,
|
|
1643
|
+
undefined,
|
|
1644
|
+
0,
|
|
1411
1645
|
);
|
|
1412
1646
|
whereBuilder += where;
|
|
1413
1647
|
bindableBuilder.push(...bindable);
|
|
@@ -1418,14 +1652,19 @@ const convertRequestToQuery = <
|
|
|
1418
1652
|
table,
|
|
1419
1653
|
joinBuilder,
|
|
1420
1654
|
path,
|
|
1655
|
+
undefined,
|
|
1656
|
+
0,
|
|
1421
1657
|
);
|
|
1422
1658
|
whereBuilder += where;
|
|
1423
1659
|
bindableBuilder.push(...bindable);
|
|
1424
1660
|
}
|
|
1425
1661
|
|
|
1426
|
-
if (isIterateRequest(request, type)) {
|
|
1662
|
+
/* if (isIterateRequest(request, type)) {
|
|
1427
1663
|
let sort = request?.sort;
|
|
1428
|
-
if (
|
|
1664
|
+
if (
|
|
1665
|
+
(!sort || (Array.isArray(sort) && sort.length === 0)) &&
|
|
1666
|
+
!options?.fetchAll
|
|
1667
|
+
) {
|
|
1429
1668
|
sort =
|
|
1430
1669
|
table.primary && path.length === 0
|
|
1431
1670
|
? [{ key: [table.primary], direction: types.SortDirection.ASC }]
|
|
@@ -1446,61 +1685,97 @@ const convertRequestToQuery = <
|
|
|
1446
1685
|
undefined,
|
|
1447
1686
|
true,
|
|
1448
1687
|
);
|
|
1449
|
-
|
|
1688
|
+
|
|
1689
|
+
for (const foreignTable of foreignTables) {
|
|
1450
1690
|
if (once) {
|
|
1451
1691
|
orderByBuilder += ", ";
|
|
1452
1692
|
}
|
|
1453
1693
|
once = true;
|
|
1454
|
-
|
|
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"}`;
|
|
1455
1698
|
}
|
|
1456
1699
|
}
|
|
1457
|
-
|
|
1458
|
-
/* orderByBuilder += request.sort
|
|
1459
|
-
.map(
|
|
1460
|
-
(sort) =>
|
|
1461
|
-
`${table.name}.${sort.key} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`
|
|
1462
|
-
)
|
|
1463
|
-
.join(", "); */
|
|
1464
1700
|
}
|
|
1465
1701
|
}
|
|
1466
|
-
}
|
|
1702
|
+
} */
|
|
1467
1703
|
const where = whereBuilder.length > 0 ? "where " + whereBuilder : undefined;
|
|
1468
1704
|
|
|
1469
1705
|
if (extraJoin && extraJoin.size > 0) {
|
|
1470
1706
|
insertMapIntoMap(joinBuilder, extraJoin);
|
|
1471
1707
|
}
|
|
1472
|
-
let join = buildJoin(joinBuilder,
|
|
1708
|
+
let { join } = buildJoin(joinBuilder, options);
|
|
1473
1709
|
|
|
1474
1710
|
const query = `${join ? join : ""} ${where ? where : ""}`;
|
|
1475
1711
|
|
|
1476
1712
|
return {
|
|
1477
1713
|
query,
|
|
1478
|
-
orderBy: orderByBuilder,
|
|
1714
|
+
/* orderBy: orderByBuilder, */
|
|
1479
1715
|
bindable: bindableBuilder,
|
|
1480
1716
|
} as R;
|
|
1481
1717
|
};
|
|
1482
1718
|
|
|
1483
1719
|
export const buildJoin = (
|
|
1484
|
-
joinBuilder: Map<string,
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1720
|
+
joinBuilder: Map<string, JoinOrRootTable>,
|
|
1721
|
+
options?: {
|
|
1722
|
+
planner?: PlanningSession;
|
|
1723
|
+
},
|
|
1724
|
+
): { join: string } => {
|
|
1725
|
+
/* let joinTypeDefault = resolveAllColumns
|
|
1726
|
+
? "CROSS JOIN"
|
|
1727
|
+
: "JOIN"; */
|
|
1490
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
|
+
}
|
|
1491
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") {
|
|
1492
1767
|
let nonInlinedParent =
|
|
1493
1768
|
table.table.parent && getNonInlinedTable(table.table.parent);
|
|
1494
1769
|
if (!nonInlinedParent) {
|
|
1495
1770
|
throw new Error("Unexpected: missing parent");
|
|
1496
1771
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
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;
|
|
1502
1776
|
}
|
|
1503
|
-
|
|
1777
|
+
|
|
1778
|
+
return { join };
|
|
1504
1779
|
};
|
|
1505
1780
|
|
|
1506
1781
|
const insertMapIntoMap = (map: Map<string, any>, insert: Map<string, any>) => {
|
|
@@ -1513,9 +1788,10 @@ export const convertQueryToSQLQuery = (
|
|
|
1513
1788
|
query: types.Query,
|
|
1514
1789
|
tables: Map<string, Table>,
|
|
1515
1790
|
table: Table,
|
|
1516
|
-
joinBuilder: Map<string,
|
|
1517
|
-
path: string[]
|
|
1518
|
-
tableAlias: string | undefined
|
|
1791
|
+
joinBuilder: Map<string, JoinOrRootTable>,
|
|
1792
|
+
path: string[],
|
|
1793
|
+
tableAlias: string | undefined,
|
|
1794
|
+
skipKeys: number,
|
|
1519
1795
|
): { where: string; bindable: any[] } => {
|
|
1520
1796
|
let whereBuilder = "";
|
|
1521
1797
|
let bindableBuilder: any[] = [];
|
|
@@ -1524,7 +1800,8 @@ export const convertQueryToSQLQuery = (
|
|
|
1524
1800
|
const handleAnd = (
|
|
1525
1801
|
queries: types.Query[],
|
|
1526
1802
|
path: string[],
|
|
1527
|
-
tableAlias
|
|
1803
|
+
tableAlias: string | undefined,
|
|
1804
|
+
keysOffset: number,
|
|
1528
1805
|
) => {
|
|
1529
1806
|
for (const query of queries) {
|
|
1530
1807
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
@@ -1534,6 +1811,7 @@ export const convertQueryToSQLQuery = (
|
|
|
1534
1811
|
joinBuilder,
|
|
1535
1812
|
path,
|
|
1536
1813
|
tableAlias,
|
|
1814
|
+
keysOffset,
|
|
1537
1815
|
);
|
|
1538
1816
|
whereBuilder =
|
|
1539
1817
|
whereBuilder.length > 0 ? `(${whereBuilder}) AND (${where})` : where;
|
|
@@ -1549,16 +1827,18 @@ export const convertQueryToSQLQuery = (
|
|
|
1549
1827
|
joinBuilder,
|
|
1550
1828
|
path,
|
|
1551
1829
|
tableAlias,
|
|
1830
|
+
skipKeys,
|
|
1552
1831
|
);
|
|
1553
1832
|
whereBuilder += where;
|
|
1554
1833
|
bindableBuilder.push(...bindable);
|
|
1555
1834
|
} else if (query instanceof types.Nested) {
|
|
1556
1835
|
let joinPrefix = "__" + String(tables.size);
|
|
1557
|
-
path = [...path, query.path];
|
|
1558
|
-
|
|
1836
|
+
path = [...path, ...query.path];
|
|
1837
|
+
let newSkipKeys = skipKeys + query.path.length;
|
|
1838
|
+
handleAnd(query.query, path, joinPrefix, newSkipKeys);
|
|
1559
1839
|
} else if (query instanceof types.LogicalQuery) {
|
|
1560
1840
|
if (query instanceof types.And) {
|
|
1561
|
-
handleAnd(query.and, path, tableAlias);
|
|
1841
|
+
handleAnd(query.and, path, tableAlias, skipKeys);
|
|
1562
1842
|
} else if (query instanceof types.Or) {
|
|
1563
1843
|
for (const subquery of query.or) {
|
|
1564
1844
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
@@ -1568,9 +1848,10 @@ export const convertQueryToSQLQuery = (
|
|
|
1568
1848
|
joinBuilder,
|
|
1569
1849
|
path,
|
|
1570
1850
|
tableAlias,
|
|
1851
|
+
skipKeys,
|
|
1571
1852
|
);
|
|
1572
1853
|
whereBuilder =
|
|
1573
|
-
whereBuilder.length > 0 ? `(${whereBuilder}) OR
|
|
1854
|
+
whereBuilder.length > 0 ? `(${whereBuilder}) OR(${where})` : where;
|
|
1574
1855
|
bindableBuilder.push(...bindable);
|
|
1575
1856
|
}
|
|
1576
1857
|
} else if (query instanceof types.Not) {
|
|
@@ -1581,8 +1862,9 @@ export const convertQueryToSQLQuery = (
|
|
|
1581
1862
|
joinBuilder,
|
|
1582
1863
|
path,
|
|
1583
1864
|
tableAlias,
|
|
1865
|
+
skipKeys,
|
|
1584
1866
|
);
|
|
1585
|
-
whereBuilder = `NOT
|
|
1867
|
+
whereBuilder = `NOT(${where})`;
|
|
1586
1868
|
bindableBuilder.push(...bindable);
|
|
1587
1869
|
} else {
|
|
1588
1870
|
throw new Error("Unsupported query type: " + query.constructor.name);
|
|
@@ -1601,38 +1883,54 @@ const cloneQuery = (query: types.StateFieldQuery) => {
|
|
|
1601
1883
|
return deserialize(serialize(query), types.StateFieldQuery);
|
|
1602
1884
|
};
|
|
1603
1885
|
|
|
1886
|
+
type JoinOrRootTable = JoinTable | RootTable;
|
|
1887
|
+
|
|
1604
1888
|
type JoinTable = {
|
|
1605
1889
|
table: Table;
|
|
1606
1890
|
as: string;
|
|
1891
|
+
type: "left" | "cross";
|
|
1892
|
+
columns: string[];
|
|
1607
1893
|
};
|
|
1608
1894
|
|
|
1609
|
-
|
|
1895
|
+
type RootTable = {
|
|
1896
|
+
type: "root";
|
|
1897
|
+
table: Table;
|
|
1898
|
+
as: string;
|
|
1899
|
+
columns: string[];
|
|
1900
|
+
};
|
|
1901
|
+
|
|
1902
|
+
/* const createQueryTableReferenceName = (
|
|
1610
1903
|
table: Table,
|
|
1611
1904
|
alias: string | undefined,
|
|
1612
|
-
fieldType: FieldType,
|
|
1613
|
-
joinSize: number,
|
|
1614
1905
|
) => {
|
|
1906
|
+
|
|
1615
1907
|
if (
|
|
1616
|
-
!alias
|
|
1617
|
-
(fieldType instanceof VecKind ||
|
|
1618
|
-
(fieldType instanceof OptionKind &&
|
|
1619
|
-
fieldType.elementType instanceof VecKind))
|
|
1908
|
+
!alias
|
|
1620
1909
|
) {
|
|
1621
|
-
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
|
|
1622
1912
|
alias = aliasSuffix;
|
|
1623
1913
|
}
|
|
1624
1914
|
const tableNameAs = alias ? alias + "_" + table.name : table.name;
|
|
1625
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; */
|
|
1626
1924
|
};
|
|
1627
1925
|
|
|
1628
1926
|
const resolveTableToQuery = (
|
|
1629
1927
|
table: Table,
|
|
1630
1928
|
tables: Map<string, Table>,
|
|
1631
|
-
join: Map<string,
|
|
1929
|
+
join: Map<string, JoinOrRootTable>,
|
|
1632
1930
|
path: string[],
|
|
1633
1931
|
alias: string | undefined,
|
|
1634
1932
|
searchSelf: boolean,
|
|
1635
|
-
) => {
|
|
1933
|
+
): { queryKey: string; foreignTables: JoinOrRootTable[] } => {
|
|
1636
1934
|
// we are matching in two ways.
|
|
1637
1935
|
|
|
1638
1936
|
// 1. joins
|
|
@@ -1649,12 +1947,19 @@ const resolveTableToQuery = (
|
|
|
1649
1947
|
if (field) {
|
|
1650
1948
|
return {
|
|
1651
1949
|
queryKey: field.name,
|
|
1652
|
-
foreignTables: [
|
|
1950
|
+
foreignTables: [getOrSetRootTable(join, table)],
|
|
1653
1951
|
};
|
|
1654
1952
|
}
|
|
1655
1953
|
}
|
|
1656
1954
|
|
|
1657
|
-
let currentTables: JoinTable[] = [
|
|
1955
|
+
let currentTables: JoinTable[] = [
|
|
1956
|
+
{
|
|
1957
|
+
table,
|
|
1958
|
+
as: alias || table.name,
|
|
1959
|
+
type: "cross" as const,
|
|
1960
|
+
columns: [],
|
|
1961
|
+
},
|
|
1962
|
+
];
|
|
1658
1963
|
let prevTables: JoinTable[] | undefined = undefined;
|
|
1659
1964
|
|
|
1660
1965
|
// outer:
|
|
@@ -1667,20 +1972,29 @@ const resolveTableToQuery = (
|
|
|
1667
1972
|
if (!field && currentTable.children.length > 0) {
|
|
1668
1973
|
// second arg is needed because of polymorphic fields we might end up here intentially to check what tables to query
|
|
1669
1974
|
throw new MissingFieldError(
|
|
1670
|
-
`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))} `,
|
|
1671
1976
|
);
|
|
1672
1977
|
}
|
|
1673
1978
|
for (const child of currentTable.children) {
|
|
1674
|
-
const tableNameAs =
|
|
1979
|
+
const tableNameAs = createQueryTableReferenceName(
|
|
1675
1980
|
child,
|
|
1676
|
-
alias,
|
|
1981
|
+
/* alias */ /* ,
|
|
1677
1982
|
field.type,
|
|
1678
|
-
join.size,
|
|
1983
|
+
join.size, */
|
|
1679
1984
|
);
|
|
1985
|
+
|
|
1680
1986
|
let isMatching =
|
|
1681
1987
|
child.parentPath![child.parentPath!.length - 1] === key;
|
|
1682
1988
|
if (isMatching) {
|
|
1683
|
-
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
|
+
};
|
|
1684
1998
|
if (child.isSimpleValue) {
|
|
1685
1999
|
if (!child.inline) {
|
|
1686
2000
|
join.set(tableNameAs, tableWithAlias);
|
|
@@ -1744,9 +2058,10 @@ const convertStateFieldQuery = (
|
|
|
1744
2058
|
query: types.StateFieldQuery,
|
|
1745
2059
|
tables: Map<string, Table>,
|
|
1746
2060
|
table: Table,
|
|
1747
|
-
join: Map<string,
|
|
2061
|
+
join: Map<string, JoinOrRootTable>,
|
|
1748
2062
|
path: string[],
|
|
1749
|
-
tableAlias: string | undefined
|
|
2063
|
+
tableAlias: string | undefined,
|
|
2064
|
+
skipKeys: number,
|
|
1750
2065
|
): { where: string; bindable: any[] } => {
|
|
1751
2066
|
// if field id represented as foreign table, do join and compare
|
|
1752
2067
|
const inlinedName = getInlineTableFieldName(query.key);
|
|
@@ -1755,11 +2070,15 @@ const convertStateFieldQuery = (
|
|
|
1755
2070
|
); /* stringArraysEquals(query.key, [...table.parentPath, x.name]) )*/
|
|
1756
2071
|
const isForeign = !tableField; // table.fields.find(x => x.name === query.key[query.key.length - 1])
|
|
1757
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
|
+
}
|
|
1758
2077
|
const { queryKey, foreignTables } = resolveTableToQuery(
|
|
1759
2078
|
table,
|
|
1760
2079
|
tables,
|
|
1761
2080
|
join,
|
|
1762
|
-
|
|
2081
|
+
tablePath,
|
|
1763
2082
|
tableAlias,
|
|
1764
2083
|
false,
|
|
1765
2084
|
);
|
|
@@ -1772,6 +2091,7 @@ const convertStateFieldQuery = (
|
|
|
1772
2091
|
if (ftable.table === table) {
|
|
1773
2092
|
throw new Error("Unexpected");
|
|
1774
2093
|
}
|
|
2094
|
+
|
|
1775
2095
|
const { where, bindable } = convertQueryToSQLQuery(
|
|
1776
2096
|
query,
|
|
1777
2097
|
tables,
|
|
@@ -1779,6 +2099,7 @@ const convertStateFieldQuery = (
|
|
|
1779
2099
|
join,
|
|
1780
2100
|
path,
|
|
1781
2101
|
ftable.as,
|
|
2102
|
+
skipKeys,
|
|
1782
2103
|
);
|
|
1783
2104
|
whereBuilder.push(where);
|
|
1784
2105
|
bindableBuilder.push(bindable);
|
|
@@ -1789,6 +2110,12 @@ const convertStateFieldQuery = (
|
|
|
1789
2110
|
};
|
|
1790
2111
|
}
|
|
1791
2112
|
|
|
2113
|
+
const columnAggregator = join.get(createQueryTableReferenceName(table))!;
|
|
2114
|
+
if (!columnAggregator) {
|
|
2115
|
+
throw new Error("Unexpected");
|
|
2116
|
+
}
|
|
2117
|
+
columnAggregator.columns.push(inlinedName);
|
|
2118
|
+
|
|
1792
2119
|
let bindable: any[] = [];
|
|
1793
2120
|
const keyWithTable =
|
|
1794
2121
|
(tableAlias || table.name) + "." + escapeColumnName(inlinedName);
|
|
@@ -1797,10 +2124,10 @@ const convertStateFieldQuery = (
|
|
|
1797
2124
|
let statement = "";
|
|
1798
2125
|
|
|
1799
2126
|
if (query.method === types.StringMatchMethod.contains) {
|
|
1800
|
-
statement = `${keyWithTable} LIKE
|
|
2127
|
+
statement = `${keyWithTable} LIKE ? `;
|
|
1801
2128
|
bindable.push(`%${query.value}%`);
|
|
1802
2129
|
} else if (query.method === types.StringMatchMethod.prefix) {
|
|
1803
|
-
statement = `${keyWithTable} LIKE
|
|
2130
|
+
statement = `${keyWithTable} LIKE ? `;
|
|
1804
2131
|
bindable.push(`${query.value}%`);
|
|
1805
2132
|
} else if (query.method === types.StringMatchMethod.exact) {
|
|
1806
2133
|
statement = `${keyWithTable} = ?`;
|
|
@@ -1819,7 +2146,7 @@ const convertStateFieldQuery = (
|
|
|
1819
2146
|
} else if (query instanceof types.IntegerCompare) {
|
|
1820
2147
|
if (tableField!.type === "BLOB") {
|
|
1821
2148
|
// TODO perf
|
|
1822
|
-
where = `hex(${keyWithTable}) LIKE
|
|
2149
|
+
where = `hex(${keyWithTable}) LIKE ? `;
|
|
1823
2150
|
bindable.push(
|
|
1824
2151
|
`%${toHexString(new Uint8Array([Number(query.value.value)]))}%`,
|
|
1825
2152
|
);
|
|
@@ -1827,15 +2154,15 @@ const convertStateFieldQuery = (
|
|
|
1827
2154
|
if (query.compare === types.Compare.Equal) {
|
|
1828
2155
|
where = `${keyWithTable} = ?`;
|
|
1829
2156
|
} else if (query.compare === types.Compare.Greater) {
|
|
1830
|
-
where = `${keyWithTable} >
|
|
2157
|
+
where = `${keyWithTable} > ? `;
|
|
1831
2158
|
} else if (query.compare === types.Compare.Less) {
|
|
1832
|
-
where = `${keyWithTable}
|
|
2159
|
+
where = `${keyWithTable} <?`;
|
|
1833
2160
|
} else if (query.compare === types.Compare.GreaterOrEqual) {
|
|
1834
|
-
where = `${keyWithTable} >=
|
|
2161
|
+
where = `${keyWithTable} >= ? `;
|
|
1835
2162
|
} else if (query.compare === types.Compare.LessOrEqual) {
|
|
1836
|
-
where = `${keyWithTable} <=
|
|
2163
|
+
where = `${keyWithTable} <= ? `;
|
|
1837
2164
|
} else {
|
|
1838
|
-
throw new Error(`Unsupported compare type: ${query.compare}`);
|
|
2165
|
+
throw new Error(`Unsupported compare type: ${query.compare} `);
|
|
1839
2166
|
}
|
|
1840
2167
|
|
|
1841
2168
|
if (unwrapNestedType(tableField.from!.type) === "u64") {
|
|
@@ -1855,3 +2182,14 @@ const convertStateFieldQuery = (
|
|
|
1855
2182
|
}
|
|
1856
2183
|
return { where, bindable };
|
|
1857
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
|
+
};
|