@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/dist/src/schema.js
CHANGED
|
@@ -8,8 +8,9 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { FixedArrayKind, OptionKind, VecKind, WrappedType, deserialize, field as fieldDecalaration, getDependencies, getSchema, serialize, variant, } from "@dao-xyz/borsh";
|
|
11
|
-
import { toHexString } from "@peerbit/crypto";
|
|
11
|
+
import { fromHexString, toHexString } from "@peerbit/crypto";
|
|
12
12
|
import * as types from "@peerbit/indexer-interface";
|
|
13
|
+
import { flattenQuery } from "./query-planner.js";
|
|
13
14
|
const SQLConversionMap = {
|
|
14
15
|
u8: "INTEGER",
|
|
15
16
|
u16: "INTEGER",
|
|
@@ -27,17 +28,27 @@ const SQLConversionMap = {
|
|
|
27
28
|
Date: "TEXT",
|
|
28
29
|
};
|
|
29
30
|
const WRAPPED_SIMPLE_VALUE_VARIANT = "wrapped";
|
|
31
|
+
let JSON_GROUP_ARRAY = "json_group_array";
|
|
32
|
+
let JSON_OBJECT = "distinct json_object";
|
|
33
|
+
export const u64ToI64 = (u64) => {
|
|
34
|
+
return (typeof u64 === "number" ? BigInt(u64) : u64) - 9223372036854775808n;
|
|
35
|
+
};
|
|
36
|
+
export const i64ToU64 = (i64) => (typeof i64 === "number" ? BigInt(i64) : i64) + 9223372036854775808n;
|
|
30
37
|
export const convertToSQLType = (value, type) => {
|
|
31
38
|
// add bigint when https://github.com/TryGhost/node-sqlite3/pull/1501 fixed
|
|
32
39
|
if (value != null) {
|
|
33
40
|
if (type === "bool") {
|
|
34
41
|
return value ? 1 : 0;
|
|
35
42
|
}
|
|
43
|
+
if (type === "u64") {
|
|
44
|
+
// shift to fit in i64
|
|
45
|
+
return u64ToI64(value);
|
|
46
|
+
}
|
|
36
47
|
}
|
|
37
48
|
return value;
|
|
38
49
|
};
|
|
39
50
|
const nullAsUndefined = (value) => (value === null ? undefined : value);
|
|
40
|
-
export const escapeColumnName = (name) =>
|
|
51
|
+
export const escapeColumnName = (name, char = '"') => `${char}${name}${char}`;
|
|
41
52
|
export class MissingFieldError extends Error {
|
|
42
53
|
constructor(message) {
|
|
43
54
|
super(message);
|
|
@@ -61,9 +72,13 @@ export const convertFromSQLType = (value, type) => {
|
|
|
61
72
|
: nullAsUndefined(value);
|
|
62
73
|
}
|
|
63
74
|
if (type === "u64") {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
76
|
+
return i64ToU64(value); // TODO is not always value type bigint?
|
|
77
|
+
}
|
|
78
|
+
if (value == null) {
|
|
79
|
+
return nullAsUndefined(value);
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`Unexpected value type for value ${value} expected number or bigint for u64 field`);
|
|
67
82
|
}
|
|
68
83
|
return nullAsUndefined(value);
|
|
69
84
|
};
|
|
@@ -115,6 +130,7 @@ export const getSQLTable = (ctor, path, primary, inline, addJoinField, fromOptio
|
|
|
115
130
|
referencedInArray: false,
|
|
116
131
|
isSimpleValue: false,
|
|
117
132
|
inline,
|
|
133
|
+
indices: new Set(),
|
|
118
134
|
};
|
|
119
135
|
ret.push(table);
|
|
120
136
|
for (const dep of dependencies) {
|
|
@@ -146,12 +162,21 @@ const getNameOfClass = (ctor) => {
|
|
|
146
162
|
return name;
|
|
147
163
|
};
|
|
148
164
|
export const getTableName = (path = [], clazz) => {
|
|
165
|
+
let pathKey = path.length > 0 ? path.join("__") + "__" : "";
|
|
166
|
+
if (typeof clazz !== "string") {
|
|
167
|
+
const tableName = clazz["__table_" + pathKey];
|
|
168
|
+
if (tableName) {
|
|
169
|
+
return tableName;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
149
172
|
let name = typeof clazz === "string" ? clazz : getNameOfClass(clazz);
|
|
150
173
|
// prefix the generated table name so that the name is a valid SQL identifier (table name)
|
|
151
174
|
// choose prefix which is readable and explains that this is a generated table name
|
|
152
175
|
// leading _ to allow path to have numbers
|
|
153
|
-
const ret =
|
|
154
|
-
|
|
176
|
+
const ret = pathKey + name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
177
|
+
if (typeof clazz !== "string") {
|
|
178
|
+
clazz["__table_" + pathKey] = ret;
|
|
179
|
+
}
|
|
155
180
|
return ret;
|
|
156
181
|
};
|
|
157
182
|
export const CHILD_TABLE_ID = "__id";
|
|
@@ -174,12 +199,12 @@ export const getSQLFields = (tableName, path, ctor, primary, addJoinFieldFromPar
|
|
|
174
199
|
? addJoinFieldFromParent
|
|
175
200
|
: (fields, contstraints) => {
|
|
176
201
|
// we resolve primary field here since it might be unknown until this point
|
|
177
|
-
const
|
|
202
|
+
const parentPrimaryField = primary != null
|
|
178
203
|
? sqlFields.find((field) => field.name === primary)
|
|
179
204
|
: undefined;
|
|
180
|
-
const parentPrimaryFieldName =
|
|
181
|
-
const parentPrimaryFieldType =
|
|
182
|
-
?
|
|
205
|
+
const parentPrimaryFieldName = parentPrimaryField?.key || CHILD_TABLE_ID;
|
|
206
|
+
const parentPrimaryFieldType = parentPrimaryField
|
|
207
|
+
? parentPrimaryField.type
|
|
183
208
|
: "INTEGER";
|
|
184
209
|
fields.unshift({
|
|
185
210
|
name: CHILD_TABLE_ID,
|
|
@@ -188,6 +213,7 @@ export const getSQLFields = (tableName, path, ctor, primary, addJoinFieldFromPar
|
|
|
188
213
|
type: "INTEGER",
|
|
189
214
|
isPrimary: true,
|
|
190
215
|
from: undefined,
|
|
216
|
+
unwrappedType: undefined,
|
|
191
217
|
path: [CHILD_TABLE_ID],
|
|
192
218
|
},
|
|
193
219
|
// foreign key parent document
|
|
@@ -196,8 +222,9 @@ export const getSQLFields = (tableName, path, ctor, primary, addJoinFieldFromPar
|
|
|
196
222
|
key: PARENT_TABLE_ID,
|
|
197
223
|
definition: `${PARENT_TABLE_ID} ${parentPrimaryFieldType}`,
|
|
198
224
|
type: parentPrimaryFieldType,
|
|
225
|
+
from: parentPrimaryField?.from,
|
|
226
|
+
unwrappedType: parentPrimaryField?.unwrappedType,
|
|
199
227
|
isPrimary: false,
|
|
200
|
-
from: undefined,
|
|
201
228
|
path: [PARENT_TABLE_ID],
|
|
202
229
|
});
|
|
203
230
|
contstraints.push({
|
|
@@ -256,6 +283,7 @@ export const getSQLFields = (tableName, path, ctor, primary, addJoinFieldFromPar
|
|
|
256
283
|
type: "INTEGER",
|
|
257
284
|
isPrimary: false,
|
|
258
285
|
from: undefined,
|
|
286
|
+
unwrappedType: undefined,
|
|
259
287
|
path: [ARRAY_INDEX_COLUMN],
|
|
260
288
|
},
|
|
261
289
|
...table.fields.slice(2),
|
|
@@ -278,6 +306,7 @@ export const getSQLFields = (tableName, path, ctor, primary, addJoinFieldFromPar
|
|
|
278
306
|
type: fieldType,
|
|
279
307
|
isPrimary,
|
|
280
308
|
from: field,
|
|
309
|
+
unwrappedType: unwrapNestedType(field.type),
|
|
281
310
|
path: [...path.slice(1), key],
|
|
282
311
|
});
|
|
283
312
|
};
|
|
@@ -352,6 +381,7 @@ export const getSQLFields = (tableName, path, ctor, primary, addJoinFieldFromPar
|
|
|
352
381
|
type: "bool",
|
|
353
382
|
isPrimary: false,
|
|
354
383
|
from: undefined,
|
|
384
|
+
unwrappedType: undefined,
|
|
355
385
|
path: [...path.slice(1), key],
|
|
356
386
|
describesExistenceOfAnother: path[path.length - 1],
|
|
357
387
|
});
|
|
@@ -438,7 +468,7 @@ const getTableFromValue = (parentTable, tables, field, value) => {
|
|
|
438
468
|
continue;
|
|
439
469
|
}
|
|
440
470
|
if (ctor) {
|
|
441
|
-
clazzName =
|
|
471
|
+
clazzName = ctor;
|
|
442
472
|
break;
|
|
443
473
|
}
|
|
444
474
|
}
|
|
@@ -527,14 +557,14 @@ export const insert = async (insertFn, obj, tables, table, fields, handleNestedC
|
|
|
527
557
|
for (const _field of subTable.fields) {
|
|
528
558
|
bindableValues.push(null);
|
|
529
559
|
}
|
|
530
|
-
bindableValues[bindableValues.length - 1] =
|
|
560
|
+
bindableValues[bindableValues.length - 1] = 0; // assign the value "false" to the exist field column
|
|
531
561
|
continue;
|
|
532
562
|
}
|
|
533
563
|
await insert((values, table) => {
|
|
534
564
|
if (table.inline) {
|
|
535
565
|
bindableValues.push(...values); // insert the bindable values into the parent bindable array
|
|
536
566
|
if (field.type instanceof OptionKind) {
|
|
537
|
-
bindableValues.push(
|
|
567
|
+
bindableValues.push(1); // assign the value "true" to the exist field column
|
|
538
568
|
}
|
|
539
569
|
return undefined;
|
|
540
570
|
}
|
|
@@ -618,16 +648,15 @@ const matchFieldInShape = (shape, path, field) => {
|
|
|
618
648
|
};
|
|
619
649
|
export const selectChildren = (childrenTable) => "select * from " + childrenTable.name + " where " + PARENT_TABLE_ID + " = ?";
|
|
620
650
|
export const generateSelectQuery = (table, selects) => {
|
|
621
|
-
return `
|
|
651
|
+
return `select ${selects.map((x) => `${x.from} as ${x.as}`).join(", ")} FROM ${table.name}`;
|
|
622
652
|
};
|
|
623
653
|
export const selectAllFieldsFromTables = (tables, shape) => {
|
|
624
654
|
const selectsPerTable = [];
|
|
625
655
|
for (const table of tables) {
|
|
626
|
-
const { selects, join: joinFromSelect } = selectAllFieldsFromTable(table, shape);
|
|
627
|
-
selectsPerTable.push({ selects, joins: joinFromSelect });
|
|
656
|
+
const { selects, join: joinFromSelect, groupBy, } = selectAllFieldsFromTable(table, shape);
|
|
657
|
+
selectsPerTable.push({ selects, joins: joinFromSelect, groupBy });
|
|
628
658
|
}
|
|
629
659
|
// pad with empty selects to make sure all selects have the same length
|
|
630
|
-
/* const maxSelects = Math.max(...selectsPerTable.map(x => x.selects.length)); */
|
|
631
660
|
let newSelects = [];
|
|
632
661
|
for (const [i, selects] of selectsPerTable.entries()) {
|
|
633
662
|
const newSelect = [];
|
|
@@ -642,10 +671,6 @@ export const selectAllFieldsFromTables = (tables, shape) => {
|
|
|
642
671
|
}
|
|
643
672
|
}
|
|
644
673
|
newSelects.push(newSelect);
|
|
645
|
-
/* let pad = 0;
|
|
646
|
-
while (select.selects.length < maxSelects) {
|
|
647
|
-
select.selects.push({ from: "NULL", as: `'pad#${++pad}'` });
|
|
648
|
-
} */
|
|
649
674
|
}
|
|
650
675
|
// also return table name
|
|
651
676
|
for (const [i, selects] of selectsPerTable.entries()) {
|
|
@@ -657,8 +682,58 @@ export const selectAllFieldsFromTable = (table, shape) => {
|
|
|
657
682
|
let stack = [{ table, shape }];
|
|
658
683
|
let join = new Map();
|
|
659
684
|
const fieldResolvers = [];
|
|
685
|
+
let groupByParentId = false;
|
|
660
686
|
for (const tableAndShape of stack) {
|
|
661
|
-
if (
|
|
687
|
+
if (tableAndShape.table.referencedInArray) {
|
|
688
|
+
let selectBuilder = `${JSON_GROUP_ARRAY}(${JSON_OBJECT}(`;
|
|
689
|
+
groupByParentId = true; // we need to group by the parent id as else we will not be returned with more than 1 result
|
|
690
|
+
let first = false;
|
|
691
|
+
const as = createReconstructReferenceName(tableAndShape.table);
|
|
692
|
+
for (const field of tableAndShape.table.fields) {
|
|
693
|
+
if ((field.isPrimary ||
|
|
694
|
+
!tableAndShape.shape ||
|
|
695
|
+
matchFieldInShape(tableAndShape.shape, [], field) ||
|
|
696
|
+
// also always include the index field
|
|
697
|
+
field.name === ARRAY_INDEX_COLUMN) &&
|
|
698
|
+
field.name !== PARENT_TABLE_ID) {
|
|
699
|
+
let resolveField = `${as}.${escapeColumnName(field.name)}`;
|
|
700
|
+
// 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
|
|
701
|
+
if (field.unwrappedType === "u64") {
|
|
702
|
+
resolveField = `CAST(${resolveField} AS TEXT)`;
|
|
703
|
+
}
|
|
704
|
+
// if field is blob we need to convert it to hex string
|
|
705
|
+
if (field.type === "BLOB") {
|
|
706
|
+
resolveField = `HEX(${resolveField})`;
|
|
707
|
+
}
|
|
708
|
+
if (first) {
|
|
709
|
+
selectBuilder += `, `;
|
|
710
|
+
}
|
|
711
|
+
first = true;
|
|
712
|
+
selectBuilder += `${escapeColumnName(field.name, "'")}, ${resolveField}`;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
selectBuilder += `)) `; // FILTER (WHERE ${tableAndShape.table.name}.${tableAndShape.table.primary} IS NOT NULL)
|
|
716
|
+
fieldResolvers.push({
|
|
717
|
+
from: selectBuilder,
|
|
718
|
+
as,
|
|
719
|
+
});
|
|
720
|
+
join.set(createReconstructReferenceName(tableAndShape.table), {
|
|
721
|
+
as,
|
|
722
|
+
table: tableAndShape.table,
|
|
723
|
+
type: "left",
|
|
724
|
+
columns: [],
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
else if (!tableAndShape.table.inline) {
|
|
728
|
+
// we end up here when we have simple joins we want to make that are not arrays, and not inlined
|
|
729
|
+
if (tableAndShape.table.parent != null) {
|
|
730
|
+
join.set(createReconstructReferenceName(tableAndShape.table), {
|
|
731
|
+
as: tableAndShape.table.name,
|
|
732
|
+
table: tableAndShape.table,
|
|
733
|
+
type: "left",
|
|
734
|
+
columns: [],
|
|
735
|
+
});
|
|
736
|
+
}
|
|
662
737
|
for (const field of tableAndShape.table.fields) {
|
|
663
738
|
if (field.isPrimary ||
|
|
664
739
|
!tableAndShape.shape ||
|
|
@@ -671,9 +746,6 @@ export const selectAllFieldsFromTable = (table, shape) => {
|
|
|
671
746
|
}
|
|
672
747
|
}
|
|
673
748
|
for (const child of tableAndShape.table.children) {
|
|
674
|
-
if (child.referencedInArray) {
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
749
|
let childShape = undefined;
|
|
678
750
|
if (tableAndShape.shape) {
|
|
679
751
|
const parentPath = child.parentPath?.slice(1);
|
|
@@ -691,15 +763,16 @@ export const selectAllFieldsFromTable = (table, shape) => {
|
|
|
691
763
|
: maybeShape;
|
|
692
764
|
}
|
|
693
765
|
stack.push({ table: child, shape: childShape });
|
|
694
|
-
if (!child.inline) {
|
|
695
|
-
join.set(child.name, { as: child.name, table: child });
|
|
696
|
-
}
|
|
697
766
|
}
|
|
698
767
|
}
|
|
699
768
|
if (fieldResolvers.length === 0) {
|
|
700
769
|
throw new Error("No fields to resolve");
|
|
701
770
|
}
|
|
702
771
|
return {
|
|
772
|
+
groupBy: groupByParentId
|
|
773
|
+
? `${table.name}.${escapeColumnName(table.primary)}` ||
|
|
774
|
+
undefined
|
|
775
|
+
: undefined,
|
|
703
776
|
selects: fieldResolvers, // `SELECT ${fieldResolvers.join(", ")} FROM ${table.name}`,
|
|
704
777
|
join,
|
|
705
778
|
};
|
|
@@ -734,14 +807,48 @@ export const resolveInstanceFromValue = async (fromTablePrefixedValues, tables,
|
|
|
734
807
|
? maybeShape[0]
|
|
735
808
|
: maybeShape;
|
|
736
809
|
if (isArray) {
|
|
737
|
-
let once = false;
|
|
810
|
+
/* let once = false; */
|
|
738
811
|
let resolvedArr = [];
|
|
739
812
|
for (const subtable of subTables) {
|
|
740
|
-
//
|
|
741
|
-
let
|
|
742
|
-
const
|
|
743
|
-
if (
|
|
744
|
-
|
|
813
|
+
// check if the array already in the provided row
|
|
814
|
+
let arr = undefined;
|
|
815
|
+
const tableName = createReconstructReferenceName(subtable);
|
|
816
|
+
if (fromTablePrefixedValues[tableName]) {
|
|
817
|
+
arr = JSON.parse(fromTablePrefixedValues[tableName]);
|
|
818
|
+
arr = arr.filter((x) => x[subtable.primary] != null);
|
|
819
|
+
// we need to go over all fields that are to be bigints and convert
|
|
820
|
+
// them back to bigints
|
|
821
|
+
// for blob fields we need to convert them back to Uint8Array
|
|
822
|
+
for (const field of subtable.fields) {
|
|
823
|
+
if (field.name === PARENT_TABLE_ID) {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
if (field.unwrappedType === "u64") {
|
|
827
|
+
for (const item of arr) {
|
|
828
|
+
item[field.name] = BigInt(item[field.name]);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
else if (field.type === "BLOB") {
|
|
832
|
+
for (const item of arr) {
|
|
833
|
+
item[field.name] = fromHexString(item[field.name]);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
if (subtable.children) {
|
|
840
|
+
// TODO we only end up where when we resolve nested arrays,
|
|
841
|
+
// which shoulld instead be resolved in a nested select (with json_group_array and json_object)
|
|
842
|
+
let rootTable = getNonInlinedTable(table);
|
|
843
|
+
const parentId = fromTablePrefixedValues[getTablePrefixedField(rootTable, rootTable.primary, !tablePrefixed)];
|
|
844
|
+
arr = await resolveChildren(parentId, subtable);
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
arr = [];
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (arr && arr.length > 0) {
|
|
851
|
+
/* once = true; */
|
|
745
852
|
for (const element of arr) {
|
|
746
853
|
const resolved = await resolveInstanceFromValue(element, tables, subtable, // TODO fix
|
|
747
854
|
resolveChildren, false, subshape);
|
|
@@ -751,12 +858,7 @@ export const resolveInstanceFromValue = async (fromTablePrefixedValues, tables,
|
|
|
751
858
|
}
|
|
752
859
|
}
|
|
753
860
|
}
|
|
754
|
-
|
|
755
|
-
obj[field.key] = undefined;
|
|
756
|
-
}
|
|
757
|
-
else {
|
|
758
|
-
obj[field.key] = resolvedArr;
|
|
759
|
-
}
|
|
861
|
+
obj[field.key] = resolvedArr; // we can not do option(vec('T')) since we dont store the option type for Arrays (TODO)
|
|
760
862
|
}
|
|
761
863
|
else {
|
|
762
864
|
// resolve nested object from row directly
|
|
@@ -845,14 +947,14 @@ export const fromRowToObj = (row, ctor) => {
|
|
|
845
947
|
return Object.assign(Object.create(ctor.prototype), obj);
|
|
846
948
|
};
|
|
847
949
|
export const convertDeleteRequestToQuery = (request, tables, table) => {
|
|
848
|
-
const { query, bindable } = convertRequestToQuery("delete", request, tables, table);
|
|
950
|
+
const { query, bindable } = convertRequestToQuery("delete", { query: types.toQuery(request.query) }, tables, table);
|
|
849
951
|
return {
|
|
850
952
|
sql: `DELETE FROM ${table.name} WHERE ${table.primary} IN (SELECT ${table.primary} from ${table.name} ${query}) returning ${table.primary}`,
|
|
851
953
|
bindable,
|
|
852
954
|
};
|
|
853
955
|
};
|
|
854
956
|
export const convertSumRequestToQuery = (request, tables, table) => {
|
|
855
|
-
const { query, bindable } = convertRequestToQuery("sum", request, tables, table);
|
|
957
|
+
const { query, bindable } = convertRequestToQuery("sum", { query: types.toQuery(request.query), key: request.key }, tables, table);
|
|
856
958
|
const inlineName = getInlineTableFieldName(request.key);
|
|
857
959
|
const field = table.fields.find((x) => x.name === inlineName);
|
|
858
960
|
if (unwrapNestedType(field.from.type) === "u64") {
|
|
@@ -865,12 +967,45 @@ export const convertSumRequestToQuery = (request, tables, table) => {
|
|
|
865
967
|
};
|
|
866
968
|
};
|
|
867
969
|
export const convertCountRequestToQuery = (request, tables, table) => {
|
|
868
|
-
const { query, bindable } = convertRequestToQuery("count", request, tables, table);
|
|
970
|
+
const { query, bindable } = convertRequestToQuery("count", { query: request?.query ? types.toQuery(request.query) : undefined }, tables, table);
|
|
869
971
|
return {
|
|
870
972
|
sql: `SELECT count(*) as count FROM ${table.name} ${query}`,
|
|
871
973
|
bindable,
|
|
872
974
|
};
|
|
873
975
|
};
|
|
976
|
+
const buildOrderBy = (sort, tables, table, joinBuilder, resolverBuilder, path = [], options) => {
|
|
977
|
+
let orderByBuilder = undefined;
|
|
978
|
+
if ((!sort || (Array.isArray(sort) && sort.length === 0)) &&
|
|
979
|
+
!options?.fetchAll) {
|
|
980
|
+
sort =
|
|
981
|
+
table.primary && path.length === 0
|
|
982
|
+
? [{ key: [table.primary], direction: types.SortDirection.ASC }]
|
|
983
|
+
: undefined;
|
|
984
|
+
}
|
|
985
|
+
if (sort) {
|
|
986
|
+
let sortArr = Array.isArray(sort) ? sort : [sort];
|
|
987
|
+
if (sortArr.length > 0) {
|
|
988
|
+
orderByBuilder = "";
|
|
989
|
+
let once = false;
|
|
990
|
+
for (const sort of sortArr) {
|
|
991
|
+
const { foreignTables, queryKey } = resolveTableToQuery(table, tables, joinBuilder, [...path, ...sort.key], undefined, true);
|
|
992
|
+
for (const foreignTable of foreignTables) {
|
|
993
|
+
if (once) {
|
|
994
|
+
orderByBuilder += ", ";
|
|
995
|
+
}
|
|
996
|
+
once = true;
|
|
997
|
+
foreignTable.columns.push(queryKey); // add the sort key to the list of columns that will be used for this query
|
|
998
|
+
orderByBuilder += `"${foreignTable.as}#${queryKey}" ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`;
|
|
999
|
+
resolverBuilder.push({
|
|
1000
|
+
from: `${table.name}.${escapeColumnName(queryKey)}`,
|
|
1001
|
+
as: `'${foreignTable.as}#${queryKey}'`,
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return { orderByBuilder };
|
|
1008
|
+
};
|
|
874
1009
|
export const convertSearchRequestToQuery = (request, tables, rootTables, options) => {
|
|
875
1010
|
let unionBuilder = "";
|
|
876
1011
|
let orderByClause = "";
|
|
@@ -879,21 +1014,19 @@ export const convertSearchRequestToQuery = (request, tables, rootTables, options
|
|
|
879
1014
|
const selectsPerTable = selectAllFieldsFromTables(rootTables, options?.shape);
|
|
880
1015
|
let bindableBuilder = [];
|
|
881
1016
|
for (const [i, table] of rootTables.entries()) {
|
|
882
|
-
const { selects, joins
|
|
883
|
-
const selectQuery = generateSelectQuery(table, selects);
|
|
1017
|
+
const { selects, joins, groupBy } = selectsPerTable[i];
|
|
884
1018
|
try {
|
|
885
|
-
const {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
:
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
bindableBuilder.push(...bindable);
|
|
1019
|
+
const { orderByBuilder } = buildOrderBy(request?.sort, tables, table, joins, selects, [], options);
|
|
1020
|
+
if (!orderByClause && orderByBuilder) {
|
|
1021
|
+
// assume all order by clauses will be the same
|
|
1022
|
+
orderByClause =
|
|
1023
|
+
orderByBuilder.length > 0
|
|
1024
|
+
? orderByClause.length > 0
|
|
1025
|
+
? orderByClause + ", " + orderByBuilder
|
|
1026
|
+
: orderByBuilder
|
|
1027
|
+
: orderByClause;
|
|
1028
|
+
}
|
|
1029
|
+
//orderByAddedOnce = true;
|
|
897
1030
|
}
|
|
898
1031
|
catch (error) {
|
|
899
1032
|
if (error instanceof MissingFieldError) {
|
|
@@ -902,138 +1035,209 @@ export const convertSearchRequestToQuery = (request, tables, rootTables, options
|
|
|
902
1035
|
}
|
|
903
1036
|
throw error;
|
|
904
1037
|
}
|
|
1038
|
+
const selectQuery = generateSelectQuery(table, selects);
|
|
1039
|
+
for (const flattenRequest of flattenQuery(request)) {
|
|
1040
|
+
try {
|
|
1041
|
+
const { query, bindable } = convertRequestToQuery("iterate", flattenRequest, tables, table, new Map(joins), // copy the map, else we might might do unececessary joins
|
|
1042
|
+
[], options);
|
|
1043
|
+
unionBuilder += `${unionBuilder.length > 0 ? " UNION " : ""} ${selectQuery} ${query} ${groupBy ? "GROUP BY " + groupBy : ""}`;
|
|
1044
|
+
matchedOnce = true;
|
|
1045
|
+
bindableBuilder.push(...bindable);
|
|
1046
|
+
}
|
|
1047
|
+
catch (error) {
|
|
1048
|
+
if (error instanceof MissingFieldError) {
|
|
1049
|
+
lastError = error;
|
|
1050
|
+
orderByClause = "";
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
throw error;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
905
1056
|
}
|
|
906
1057
|
if (!matchedOnce) {
|
|
907
1058
|
throw lastError;
|
|
908
1059
|
}
|
|
909
1060
|
return {
|
|
910
|
-
sql: `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} limit ? offset
|
|
1061
|
+
sql: `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} ${options?.fetchAll ? "" : "limit ? offset ?"}`,
|
|
911
1062
|
bindable: bindableBuilder,
|
|
912
1063
|
};
|
|
913
1064
|
};
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1065
|
+
const getOrSetRootTable = (joinBuilder, table) => {
|
|
1066
|
+
const refName = createQueryTableReferenceName(table);
|
|
1067
|
+
let ref = joinBuilder.get(refName);
|
|
1068
|
+
if (ref) {
|
|
1069
|
+
return ref;
|
|
1070
|
+
}
|
|
1071
|
+
const join = {
|
|
1072
|
+
// add the root as a join even though it is not, just so we can collect the columns it will be queried
|
|
1073
|
+
table: table,
|
|
1074
|
+
type: "root",
|
|
1075
|
+
as: table.name,
|
|
1076
|
+
columns: [],
|
|
1077
|
+
};
|
|
1078
|
+
joinBuilder.set(refName, join);
|
|
1079
|
+
return join;
|
|
1080
|
+
};
|
|
917
1081
|
const convertRequestToQuery = (type, request, tables, table, extraJoin, path = [], options) => {
|
|
918
1082
|
let whereBuilder = "";
|
|
919
1083
|
let bindableBuilder = [];
|
|
920
|
-
let orderByBuilder = undefined;
|
|
1084
|
+
/* let orderByBuilder: string | undefined = undefined; */
|
|
921
1085
|
/* let tablesToSelect: string[] = [table.name]; */
|
|
922
1086
|
let joinBuilder = extraJoin || new Map();
|
|
1087
|
+
getOrSetRootTable(joinBuilder, table);
|
|
923
1088
|
const coercedQuery = types.toQuery(request?.query);
|
|
924
1089
|
if (coercedQuery.length === 1) {
|
|
925
|
-
const { where, bindable } = convertQueryToSQLQuery(coercedQuery[0], tables, table, joinBuilder, path);
|
|
1090
|
+
const { where, bindable } = convertQueryToSQLQuery(coercedQuery[0], tables, table, joinBuilder, path, undefined, 0);
|
|
926
1091
|
whereBuilder += where;
|
|
927
1092
|
bindableBuilder.push(...bindable);
|
|
928
1093
|
}
|
|
929
1094
|
else if (coercedQuery.length > 1) {
|
|
930
|
-
const { where, bindable } = convertQueryToSQLQuery(new types.And(coercedQuery), tables, table, joinBuilder, path);
|
|
1095
|
+
const { where, bindable } = convertQueryToSQLQuery(new types.And(coercedQuery), tables, table, joinBuilder, path, undefined, 0);
|
|
931
1096
|
whereBuilder += where;
|
|
932
1097
|
bindableBuilder.push(...bindable);
|
|
933
1098
|
}
|
|
934
|
-
if (isIterateRequest(request, type)) {
|
|
1099
|
+
/* if (isIterateRequest(request, type)) {
|
|
935
1100
|
let sort = request?.sort;
|
|
936
|
-
if (
|
|
1101
|
+
if (
|
|
1102
|
+
(!sort || (Array.isArray(sort) && sort.length === 0)) &&
|
|
1103
|
+
!options?.fetchAll
|
|
1104
|
+
) {
|
|
937
1105
|
sort =
|
|
938
1106
|
table.primary && path.length === 0
|
|
939
1107
|
? [{ key: [table.primary], direction: types.SortDirection.ASC }]
|
|
940
1108
|
: undefined;
|
|
941
1109
|
}
|
|
1110
|
+
|
|
942
1111
|
if (sort) {
|
|
943
1112
|
let sortArr = Array.isArray(sort) ? sort : [sort];
|
|
944
1113
|
if (sortArr.length > 0) {
|
|
945
1114
|
orderByBuilder = "";
|
|
946
1115
|
let once = false;
|
|
947
1116
|
for (const sort of sortArr) {
|
|
948
|
-
const { foreignTables, queryKey } = resolveTableToQuery(
|
|
949
|
-
|
|
1117
|
+
const { foreignTables, queryKey } = resolveTableToQuery(
|
|
1118
|
+
table,
|
|
1119
|
+
tables,
|
|
1120
|
+
joinBuilder,
|
|
1121
|
+
[...path, ...sort.key],
|
|
1122
|
+
undefined,
|
|
1123
|
+
true,
|
|
1124
|
+
);
|
|
1125
|
+
|
|
1126
|
+
for (const foreignTable of foreignTables) {
|
|
950
1127
|
if (once) {
|
|
951
1128
|
orderByBuilder += ", ";
|
|
952
1129
|
}
|
|
953
1130
|
once = true;
|
|
954
|
-
|
|
1131
|
+
|
|
1132
|
+
foreignTable.columns.push(queryKey); // add the sort key to the list of columns that will be used for this query
|
|
1133
|
+
|
|
1134
|
+
orderByBuilder += `${foreignTable.as}.${queryKey} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`;
|
|
955
1135
|
}
|
|
956
1136
|
}
|
|
957
|
-
/* orderByBuilder += request.sort
|
|
958
|
-
.map(
|
|
959
|
-
(sort) =>
|
|
960
|
-
`${table.name}.${sort.key} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`
|
|
961
|
-
)
|
|
962
|
-
.join(", "); */
|
|
963
1137
|
}
|
|
964
1138
|
}
|
|
965
|
-
}
|
|
1139
|
+
} */
|
|
966
1140
|
const where = whereBuilder.length > 0 ? "where " + whereBuilder : undefined;
|
|
967
1141
|
if (extraJoin && extraJoin.size > 0) {
|
|
968
1142
|
insertMapIntoMap(joinBuilder, extraJoin);
|
|
969
1143
|
}
|
|
970
|
-
let join = buildJoin(joinBuilder,
|
|
1144
|
+
let { join } = buildJoin(joinBuilder, options);
|
|
971
1145
|
const query = `${join ? join : ""} ${where ? where : ""}`;
|
|
972
1146
|
return {
|
|
973
1147
|
query,
|
|
974
|
-
orderBy: orderByBuilder,
|
|
1148
|
+
/* orderBy: orderByBuilder, */
|
|
975
1149
|
bindable: bindableBuilder,
|
|
976
1150
|
};
|
|
977
1151
|
};
|
|
978
|
-
export const buildJoin = (joinBuilder,
|
|
979
|
-
let joinTypeDefault = resolveAllColumns
|
|
980
|
-
?
|
|
981
|
-
: "JOIN";
|
|
1152
|
+
export const buildJoin = (joinBuilder, options) => {
|
|
1153
|
+
/* let joinTypeDefault = resolveAllColumns
|
|
1154
|
+
? "CROSS JOIN"
|
|
1155
|
+
: "JOIN"; */
|
|
982
1156
|
let join = "";
|
|
983
1157
|
for (const [_key, table] of joinBuilder) {
|
|
1158
|
+
if (table.type !== "root") {
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
const out = _buildJoin(table, options);
|
|
1162
|
+
join += out.join;
|
|
1163
|
+
}
|
|
1164
|
+
for (const [_key, table] of joinBuilder) {
|
|
1165
|
+
if (table.type === "root") {
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
const out = _buildJoin(table, options);
|
|
1169
|
+
join += out.join;
|
|
1170
|
+
}
|
|
1171
|
+
return { join };
|
|
1172
|
+
};
|
|
1173
|
+
const _buildJoin = (table, options) => {
|
|
1174
|
+
let join = "";
|
|
1175
|
+
let indexedBy = undefined;
|
|
1176
|
+
if (table.type !== "root") {
|
|
1177
|
+
table.columns.push(PARENT_TABLE_ID); // we unshift because we join on the parent id before where clause
|
|
1178
|
+
}
|
|
1179
|
+
if (table.columns.length > 0) {
|
|
1180
|
+
const usedColumns = removeDuplicatesOrdered(table.columns);
|
|
1181
|
+
indexedBy = options?.planner
|
|
1182
|
+
? ` INDEXED BY ${options.planner.resolveIndex(table.table.name, usedColumns)} `
|
|
1183
|
+
: "";
|
|
1184
|
+
}
|
|
1185
|
+
if (table.type !== "root") {
|
|
984
1186
|
let nonInlinedParent = table.table.parent && getNonInlinedTable(table.table.parent);
|
|
985
1187
|
if (!nonInlinedParent) {
|
|
986
1188
|
throw new Error("Unexpected: missing parent");
|
|
987
1189
|
}
|
|
988
|
-
let joinType = table.
|
|
989
|
-
|
|
990
|
-
: joinTypeDefault;
|
|
991
|
-
join += `${joinType} ${table.table.name} AS ${table.as} ON ${nonInlinedParent.name}.${nonInlinedParent.primary} = ${table.as}.${PARENT_TABLE_ID} `;
|
|
1190
|
+
let joinType = table.type === "cross" ? "LEFT JOIN" : "LEFT JOIN";
|
|
1191
|
+
join += ` ${joinType} ${table.table.name} AS ${table.as} ${indexedBy} ON ${nonInlinedParent.name}.${nonInlinedParent.primary} = ${table.as}.${PARENT_TABLE_ID} `;
|
|
992
1192
|
}
|
|
993
|
-
|
|
1193
|
+
else if (indexedBy) {
|
|
1194
|
+
join += indexedBy;
|
|
1195
|
+
}
|
|
1196
|
+
return { join };
|
|
994
1197
|
};
|
|
995
1198
|
const insertMapIntoMap = (map, insert) => {
|
|
996
1199
|
for (const [key, value] of insert) {
|
|
997
1200
|
map.set(key, value);
|
|
998
1201
|
}
|
|
999
1202
|
};
|
|
1000
|
-
export const convertQueryToSQLQuery = (query, tables, table, joinBuilder, path
|
|
1203
|
+
export const convertQueryToSQLQuery = (query, tables, table, joinBuilder, path, tableAlias, skipKeys) => {
|
|
1001
1204
|
let whereBuilder = "";
|
|
1002
1205
|
let bindableBuilder = [];
|
|
1003
1206
|
/* let tablesToSelect: string[] = []; */
|
|
1004
|
-
const handleAnd = (queries, path, tableAlias) => {
|
|
1207
|
+
const handleAnd = (queries, path, tableAlias, keysOffset) => {
|
|
1005
1208
|
for (const query of queries) {
|
|
1006
|
-
const { where, bindable } = convertQueryToSQLQuery(query, tables, table, joinBuilder, path, tableAlias);
|
|
1209
|
+
const { where, bindable } = convertQueryToSQLQuery(query, tables, table, joinBuilder, path, tableAlias, keysOffset);
|
|
1007
1210
|
whereBuilder =
|
|
1008
1211
|
whereBuilder.length > 0 ? `(${whereBuilder}) AND (${where})` : where;
|
|
1009
1212
|
bindableBuilder.push(...bindable);
|
|
1010
1213
|
}
|
|
1011
1214
|
};
|
|
1012
1215
|
if (query instanceof types.StateFieldQuery) {
|
|
1013
|
-
const { where, bindable } = convertStateFieldQuery(query, tables, table, joinBuilder, path, tableAlias);
|
|
1216
|
+
const { where, bindable } = convertStateFieldQuery(query, tables, table, joinBuilder, path, tableAlias, skipKeys);
|
|
1014
1217
|
whereBuilder += where;
|
|
1015
1218
|
bindableBuilder.push(...bindable);
|
|
1016
1219
|
}
|
|
1017
1220
|
else if (query instanceof types.Nested) {
|
|
1018
1221
|
let joinPrefix = "__" + String(tables.size);
|
|
1019
|
-
path = [...path, query.path];
|
|
1020
|
-
|
|
1222
|
+
path = [...path, ...query.path];
|
|
1223
|
+
let newSkipKeys = skipKeys + query.path.length;
|
|
1224
|
+
handleAnd(query.query, path, joinPrefix, newSkipKeys);
|
|
1021
1225
|
}
|
|
1022
1226
|
else if (query instanceof types.LogicalQuery) {
|
|
1023
1227
|
if (query instanceof types.And) {
|
|
1024
|
-
handleAnd(query.and, path, tableAlias);
|
|
1228
|
+
handleAnd(query.and, path, tableAlias, skipKeys);
|
|
1025
1229
|
}
|
|
1026
1230
|
else if (query instanceof types.Or) {
|
|
1027
1231
|
for (const subquery of query.or) {
|
|
1028
|
-
const { where, bindable } = convertQueryToSQLQuery(subquery, tables, table, joinBuilder, path, tableAlias);
|
|
1232
|
+
const { where, bindable } = convertQueryToSQLQuery(subquery, tables, table, joinBuilder, path, tableAlias, skipKeys);
|
|
1029
1233
|
whereBuilder =
|
|
1030
|
-
whereBuilder.length > 0 ? `(${whereBuilder}) OR
|
|
1234
|
+
whereBuilder.length > 0 ? `(${whereBuilder}) OR(${where})` : where;
|
|
1031
1235
|
bindableBuilder.push(...bindable);
|
|
1032
1236
|
}
|
|
1033
1237
|
}
|
|
1034
1238
|
else if (query instanceof types.Not) {
|
|
1035
|
-
const { where, bindable } = convertQueryToSQLQuery(query.not, tables, table, joinBuilder, path, tableAlias);
|
|
1036
|
-
whereBuilder = `NOT
|
|
1239
|
+
const { where, bindable } = convertQueryToSQLQuery(query.not, tables, table, joinBuilder, path, tableAlias, skipKeys);
|
|
1240
|
+
whereBuilder = `NOT(${where})`;
|
|
1037
1241
|
bindableBuilder.push(...bindable);
|
|
1038
1242
|
}
|
|
1039
1243
|
else {
|
|
@@ -1051,16 +1255,26 @@ export const convertQueryToSQLQuery = (query, tables, table, joinBuilder, path =
|
|
|
1051
1255
|
const cloneQuery = (query) => {
|
|
1052
1256
|
return deserialize(serialize(query), types.StateFieldQuery);
|
|
1053
1257
|
};
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1258
|
+
/* const createQueryTableReferenceName = (
|
|
1259
|
+
table: Table,
|
|
1260
|
+
alias: string | undefined,
|
|
1261
|
+
) => {
|
|
1262
|
+
|
|
1263
|
+
if (
|
|
1264
|
+
!alias
|
|
1265
|
+
) {
|
|
1266
|
+
let aliasSuffix =
|
|
1267
|
+
"_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
|
|
1060
1268
|
alias = aliasSuffix;
|
|
1061
1269
|
}
|
|
1062
1270
|
const tableNameAs = alias ? alias + "_" + table.name : table.name;
|
|
1063
1271
|
return tableNameAs;
|
|
1272
|
+
}; */
|
|
1273
|
+
const createQueryTableReferenceName = (table) => {
|
|
1274
|
+
return table.parent == null ? table.name : "_query_" + table.name;
|
|
1275
|
+
};
|
|
1276
|
+
const createReconstructReferenceName = (table) => {
|
|
1277
|
+
return table.name; /* table.parent == null ? table.name : "_rec_" + table.name; */
|
|
1064
1278
|
};
|
|
1065
1279
|
const resolveTableToQuery = (table, tables, join, path, alias, searchSelf) => {
|
|
1066
1280
|
// we are matching in two ways.
|
|
@@ -1076,11 +1290,18 @@ const resolveTableToQuery = (table, tables, join, path, alias, searchSelf) => {
|
|
|
1076
1290
|
if (field) {
|
|
1077
1291
|
return {
|
|
1078
1292
|
queryKey: field.name,
|
|
1079
|
-
foreignTables: [
|
|
1293
|
+
foreignTables: [getOrSetRootTable(join, table)],
|
|
1080
1294
|
};
|
|
1081
1295
|
}
|
|
1082
1296
|
}
|
|
1083
|
-
let currentTables = [
|
|
1297
|
+
let currentTables = [
|
|
1298
|
+
{
|
|
1299
|
+
table,
|
|
1300
|
+
as: alias || table.name,
|
|
1301
|
+
type: "cross",
|
|
1302
|
+
columns: [],
|
|
1303
|
+
},
|
|
1304
|
+
];
|
|
1084
1305
|
let prevTables = undefined;
|
|
1085
1306
|
// outer:
|
|
1086
1307
|
for (const [_i, key] of path /* .slice(0, -1) */
|
|
@@ -1091,13 +1312,20 @@ const resolveTableToQuery = (table, tables, join, path, alias, searchSelf) => {
|
|
|
1091
1312
|
const field = schema.fields.find((x) => x.key === key);
|
|
1092
1313
|
if (!field && currentTable.children.length > 0) {
|
|
1093
1314
|
// second arg is needed because of polymorphic fields we might end up here intentially to check what tables to query
|
|
1094
|
-
throw new MissingFieldError(`Property with key "${key}" is not found in the schema ${JSON.stringify(schema.fields.map((x) => x.key))}`);
|
|
1315
|
+
throw new MissingFieldError(`Property with key "${key}" is not found in the schema ${JSON.stringify(schema.fields.map((x) => x.key))} `);
|
|
1095
1316
|
}
|
|
1096
1317
|
for (const child of currentTable.children) {
|
|
1097
|
-
const tableNameAs =
|
|
1318
|
+
const tableNameAs = createQueryTableReferenceName(child);
|
|
1098
1319
|
let isMatching = child.parentPath[child.parentPath.length - 1] === key;
|
|
1099
1320
|
if (isMatching) {
|
|
1100
|
-
const tableWithAlias = {
|
|
1321
|
+
const tableWithAlias = {
|
|
1322
|
+
columns: [],
|
|
1323
|
+
table: child,
|
|
1324
|
+
as: tableNameAs,
|
|
1325
|
+
type: currentTable.children.length > 1
|
|
1326
|
+
? "left"
|
|
1327
|
+
: "cross",
|
|
1328
|
+
};
|
|
1101
1329
|
if (child.isSimpleValue) {
|
|
1102
1330
|
if (!child.inline) {
|
|
1103
1331
|
join.set(tableNameAs, tableWithAlias);
|
|
@@ -1144,13 +1372,17 @@ const resolveTableToQuery = (table, tables, join, path, alias, searchSelf) => {
|
|
|
1144
1372
|
: FOREIGN_VALUE_PROPERTY;
|
|
1145
1373
|
return { queryKey, foreignTables };
|
|
1146
1374
|
};
|
|
1147
|
-
const convertStateFieldQuery = (query, tables, table, join, path, tableAlias
|
|
1375
|
+
const convertStateFieldQuery = (query, tables, table, join, path, tableAlias, skipKeys) => {
|
|
1148
1376
|
// if field id represented as foreign table, do join and compare
|
|
1149
1377
|
const inlinedName = getInlineTableFieldName(query.key);
|
|
1150
1378
|
const tableField = table.fields.find((x) => x.name === inlinedName); /* stringArraysEquals(query.key, [...table.parentPath, x.name]) )*/
|
|
1151
1379
|
const isForeign = !tableField; // table.fields.find(x => x.name === query.key[query.key.length - 1])
|
|
1152
1380
|
if (isForeign) {
|
|
1153
|
-
const
|
|
1381
|
+
const tablePath = [...path];
|
|
1382
|
+
for (let i = skipKeys; i < query.key.length; i++) {
|
|
1383
|
+
tablePath.push(query.key[i]);
|
|
1384
|
+
}
|
|
1385
|
+
const { queryKey, foreignTables } = resolveTableToQuery(table, tables, join, tablePath, tableAlias, false);
|
|
1154
1386
|
query = cloneQuery(query);
|
|
1155
1387
|
query.key = [queryKey];
|
|
1156
1388
|
let whereBuilder = [];
|
|
@@ -1159,7 +1391,7 @@ const convertStateFieldQuery = (query, tables, table, join, path, tableAlias = u
|
|
|
1159
1391
|
if (ftable.table === table) {
|
|
1160
1392
|
throw new Error("Unexpected");
|
|
1161
1393
|
}
|
|
1162
|
-
const { where, bindable } = convertQueryToSQLQuery(query, tables, ftable.table, join, path, ftable.as);
|
|
1394
|
+
const { where, bindable } = convertQueryToSQLQuery(query, tables, ftable.table, join, path, ftable.as, skipKeys);
|
|
1163
1395
|
whereBuilder.push(where);
|
|
1164
1396
|
bindableBuilder.push(bindable);
|
|
1165
1397
|
}
|
|
@@ -1168,17 +1400,22 @@ const convertStateFieldQuery = (query, tables, table, join, path, tableAlias = u
|
|
|
1168
1400
|
bindable: bindableBuilder.flat(),
|
|
1169
1401
|
};
|
|
1170
1402
|
}
|
|
1403
|
+
const columnAggregator = join.get(createQueryTableReferenceName(table));
|
|
1404
|
+
if (!columnAggregator) {
|
|
1405
|
+
throw new Error("Unexpected");
|
|
1406
|
+
}
|
|
1407
|
+
columnAggregator.columns.push(inlinedName);
|
|
1171
1408
|
let bindable = [];
|
|
1172
1409
|
const keyWithTable = (tableAlias || table.name) + "." + escapeColumnName(inlinedName);
|
|
1173
1410
|
let where;
|
|
1174
1411
|
if (query instanceof types.StringMatch) {
|
|
1175
1412
|
let statement = "";
|
|
1176
1413
|
if (query.method === types.StringMatchMethod.contains) {
|
|
1177
|
-
statement = `${keyWithTable} LIKE
|
|
1414
|
+
statement = `${keyWithTable} LIKE ? `;
|
|
1178
1415
|
bindable.push(`%${query.value}%`);
|
|
1179
1416
|
}
|
|
1180
1417
|
else if (query.method === types.StringMatchMethod.prefix) {
|
|
1181
|
-
statement = `${keyWithTable} LIKE
|
|
1418
|
+
statement = `${keyWithTable} LIKE ? `;
|
|
1182
1419
|
bindable.push(`${query.value}%`);
|
|
1183
1420
|
}
|
|
1184
1421
|
else if (query.method === types.StringMatchMethod.exact) {
|
|
@@ -1199,7 +1436,7 @@ const convertStateFieldQuery = (query, tables, table, join, path, tableAlias = u
|
|
|
1199
1436
|
else if (query instanceof types.IntegerCompare) {
|
|
1200
1437
|
if (tableField.type === "BLOB") {
|
|
1201
1438
|
// TODO perf
|
|
1202
|
-
where = `hex(${keyWithTable}) LIKE
|
|
1439
|
+
where = `hex(${keyWithTable}) LIKE ? `;
|
|
1203
1440
|
bindable.push(`%${toHexString(new Uint8Array([Number(query.value.value)]))}%`);
|
|
1204
1441
|
}
|
|
1205
1442
|
else {
|
|
@@ -1207,23 +1444,23 @@ const convertStateFieldQuery = (query, tables, table, join, path, tableAlias = u
|
|
|
1207
1444
|
where = `${keyWithTable} = ?`;
|
|
1208
1445
|
}
|
|
1209
1446
|
else if (query.compare === types.Compare.Greater) {
|
|
1210
|
-
where = `${keyWithTable} >
|
|
1447
|
+
where = `${keyWithTable} > ? `;
|
|
1211
1448
|
}
|
|
1212
1449
|
else if (query.compare === types.Compare.Less) {
|
|
1213
|
-
where = `${keyWithTable}
|
|
1450
|
+
where = `${keyWithTable} <?`;
|
|
1214
1451
|
}
|
|
1215
1452
|
else if (query.compare === types.Compare.GreaterOrEqual) {
|
|
1216
|
-
where = `${keyWithTable} >=
|
|
1453
|
+
where = `${keyWithTable} >= ? `;
|
|
1217
1454
|
}
|
|
1218
1455
|
else if (query.compare === types.Compare.LessOrEqual) {
|
|
1219
|
-
where = `${keyWithTable} <=
|
|
1456
|
+
where = `${keyWithTable} <= ? `;
|
|
1220
1457
|
}
|
|
1221
1458
|
else {
|
|
1222
|
-
throw new Error(`Unsupported compare type: ${query.compare}`);
|
|
1459
|
+
throw new Error(`Unsupported compare type: ${query.compare} `);
|
|
1223
1460
|
}
|
|
1224
1461
|
if (unwrapNestedType(tableField.from.type) === "u64") {
|
|
1225
1462
|
// shift left because that is how we insert the value
|
|
1226
|
-
bindable.push(query.value.value);
|
|
1463
|
+
bindable.push(u64ToI64(query.value.value));
|
|
1227
1464
|
}
|
|
1228
1465
|
else {
|
|
1229
1466
|
bindable.push(query.value.value);
|
|
@@ -1242,4 +1479,14 @@ const convertStateFieldQuery = (query, tables, table, join, path, tableAlias = u
|
|
|
1242
1479
|
}
|
|
1243
1480
|
return { where, bindable };
|
|
1244
1481
|
};
|
|
1482
|
+
const removeDuplicatesOrdered = (arr) => {
|
|
1483
|
+
let seen = new Set();
|
|
1484
|
+
return arr.filter((item) => {
|
|
1485
|
+
if (seen.has(item)) {
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1488
|
+
seen.add(item);
|
|
1489
|
+
return true;
|
|
1490
|
+
});
|
|
1491
|
+
};
|
|
1245
1492
|
//# sourceMappingURL=schema.js.map
|