@peerbit/indexer-sqlite3 1.0.2 → 1.0.3
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.min.js +148 -44
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +74 -27
- package/dist/src/engine.js.map +1 -1
- package/dist/src/schema.d.ts +20 -3
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +99 -21
- package/dist/src/schema.js.map +1 -1
- package/package.json +2 -2
- package/src/engine.ts +94 -39
- package/src/schema.ts +134 -39
package/src/schema.ts
CHANGED
|
@@ -72,6 +72,13 @@ export const convertToSQLType = (
|
|
|
72
72
|
const nullAsUndefined = (value: any) => (value === null ? undefined : value);
|
|
73
73
|
export const escapeColumnName = (name: string) => `"${name}"`;
|
|
74
74
|
|
|
75
|
+
export class MissingFieldError extends Error {
|
|
76
|
+
constructor(message: string) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.name = "MissingFieldError";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
export const convertFromSQLType = (
|
|
76
83
|
value: boolean | bigint | string | number | Uint8Array,
|
|
77
84
|
type?: FieldType,
|
|
@@ -814,10 +821,22 @@ export const getTablePrefixedField = (
|
|
|
814
821
|
`${skipPrefix ? "" : table.name + "#"}${getInlineTableFieldName(table.path.slice(1), key)}`;
|
|
815
822
|
export const getTableNameFromPrefixedField = (prefixedField: string) =>
|
|
816
823
|
prefixedField.split("#")[0];
|
|
824
|
+
|
|
817
825
|
export const getInlineTableFieldName = (
|
|
818
826
|
path: string[] | undefined,
|
|
819
|
-
key
|
|
820
|
-
) =>
|
|
827
|
+
key?: string,
|
|
828
|
+
) => {
|
|
829
|
+
if (key) {
|
|
830
|
+
return path && path.length > 0 ? `${path.join("_")}__${key}` : key;
|
|
831
|
+
} else {
|
|
832
|
+
// last element in the path is the key, the rest is the path
|
|
833
|
+
// join key with __ , rest with _
|
|
834
|
+
|
|
835
|
+
return path!.length > 2
|
|
836
|
+
? `${path!.slice(0, -1).join("_")}__${path![path!.length - 1]}`
|
|
837
|
+
: path!.join("__");
|
|
838
|
+
}
|
|
839
|
+
};
|
|
821
840
|
|
|
822
841
|
const matchFieldInShape = (
|
|
823
842
|
shape: types.Shape | undefined,
|
|
@@ -851,13 +870,73 @@ const matchFieldInShape = (
|
|
|
851
870
|
export const selectChildren = (childrenTable: Table) =>
|
|
852
871
|
"select * from " + childrenTable.name + " where " + PARENT_TABLE_ID + " = ?";
|
|
853
872
|
|
|
854
|
-
export const
|
|
873
|
+
export const generateSelectQuery = (
|
|
874
|
+
table: Table,
|
|
875
|
+
selects: { from: string; as: string }[],
|
|
876
|
+
) => {
|
|
877
|
+
return `SELECT ${selects.map((x) => `${x.from} as ${x.as}`).join(", ")} FROM ${table.name}`;
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
export const selectAllFieldsFromTables = (
|
|
881
|
+
tables: Table[],
|
|
882
|
+
shape: types.Shape | undefined,
|
|
883
|
+
) => {
|
|
884
|
+
const selectsPerTable: {
|
|
885
|
+
selects: {
|
|
886
|
+
from: string;
|
|
887
|
+
as: string;
|
|
888
|
+
}[];
|
|
889
|
+
joins: Map<string, JoinTable>;
|
|
890
|
+
}[] = [];
|
|
891
|
+
|
|
892
|
+
for (const table of tables) {
|
|
893
|
+
const { selects, join: joinFromSelect } = selectAllFieldsFromTable(
|
|
894
|
+
table,
|
|
895
|
+
shape,
|
|
896
|
+
);
|
|
897
|
+
selectsPerTable.push({ selects, joins: joinFromSelect });
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// pad with empty selects to make sure all selects have the same length
|
|
901
|
+
/* const maxSelects = Math.max(...selectsPerTable.map(x => x.selects.length)); */
|
|
902
|
+
|
|
903
|
+
let newSelects: {
|
|
904
|
+
from: string;
|
|
905
|
+
as: string;
|
|
906
|
+
}[][] = [];
|
|
907
|
+
for (const [i, selects] of selectsPerTable.entries()) {
|
|
908
|
+
const newSelect = [];
|
|
909
|
+
for (const [j, selectsOther] of selectsPerTable.entries()) {
|
|
910
|
+
if (i !== j) {
|
|
911
|
+
for (const select of selectsOther.selects) {
|
|
912
|
+
newSelect.push({ from: "NULL", as: select.as });
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
selects.selects.forEach((select) => newSelect.push(select));
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
newSelects.push(newSelect);
|
|
919
|
+
|
|
920
|
+
/* let pad = 0;
|
|
921
|
+
while (select.selects.length < maxSelects) {
|
|
922
|
+
select.selects.push({ from: "NULL", as: `'pad#${++pad}'` });
|
|
923
|
+
} */
|
|
924
|
+
}
|
|
925
|
+
// also return table name
|
|
926
|
+
for (const [i, selects] of selectsPerTable.entries()) {
|
|
927
|
+
selects.selects = newSelects[i];
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
return selectsPerTable;
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
export const selectAllFieldsFromTable = (
|
|
855
934
|
table: Table,
|
|
856
935
|
shape: types.Shape | undefined,
|
|
857
936
|
) => {
|
|
858
937
|
let stack: { table: Table; shape?: types.Shape }[] = [{ table, shape }];
|
|
859
938
|
let join: Map<string, JoinTable> = new Map();
|
|
860
|
-
const fieldResolvers: string[] = [];
|
|
939
|
+
const fieldResolvers: { from: string; as: string }[] = [];
|
|
861
940
|
for (const tableAndShape of stack) {
|
|
862
941
|
if (!tableAndShape.table.inline) {
|
|
863
942
|
for (const field of tableAndShape.table.fields) {
|
|
@@ -866,8 +945,10 @@ export const selectAllFields = (
|
|
|
866
945
|
!tableAndShape.shape ||
|
|
867
946
|
matchFieldInShape(tableAndShape.shape, [], field)
|
|
868
947
|
) {
|
|
869
|
-
|
|
870
|
-
|
|
948
|
+
fieldResolvers.push({
|
|
949
|
+
from: `${tableAndShape.table.name}.${escapeColumnName(field.name)}`,
|
|
950
|
+
as: `'${getTablePrefixedField(tableAndShape.table, field.name)}'`,
|
|
951
|
+
});
|
|
871
952
|
}
|
|
872
953
|
}
|
|
873
954
|
}
|
|
@@ -903,7 +984,7 @@ export const selectAllFields = (
|
|
|
903
984
|
}
|
|
904
985
|
|
|
905
986
|
return {
|
|
906
|
-
|
|
987
|
+
selects: fieldResolvers, // `SELECT ${fieldResolvers.join(", ")} FROM ${table.name}`,
|
|
907
988
|
join,
|
|
908
989
|
};
|
|
909
990
|
};
|
|
@@ -1120,7 +1201,7 @@ export const convertSumRequestToQuery = (
|
|
|
1120
1201
|
tables: Map<string, Table>,
|
|
1121
1202
|
table: Table,
|
|
1122
1203
|
) => {
|
|
1123
|
-
return `SELECT SUM(${table.name}.${request.key
|
|
1204
|
+
return `SELECT SUM(${table.name}.${getInlineTableFieldName(request.key)}) as sum FROM ${table.name} ${convertRequestToQuery(request, tables, table).query}`;
|
|
1124
1205
|
};
|
|
1125
1206
|
|
|
1126
1207
|
export const convertCountRequestToQuery = (
|
|
@@ -1138,23 +1219,45 @@ export const convertSearchRequestToQuery = (
|
|
|
1138
1219
|
shape: types.Shape | undefined,
|
|
1139
1220
|
) => {
|
|
1140
1221
|
let unionBuilder = "";
|
|
1141
|
-
let orderByClause: string
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1222
|
+
let orderByClause: string = "";
|
|
1223
|
+
|
|
1224
|
+
let matchedOnce = false;
|
|
1225
|
+
let lastError: Error | undefined = undefined;
|
|
1226
|
+
|
|
1227
|
+
const selectsPerTable = selectAllFieldsFromTables(rootTables, shape);
|
|
1228
|
+
|
|
1229
|
+
for (const [i, table] of rootTables.entries()) {
|
|
1230
|
+
const { selects, joins: joinFromSelect } = selectsPerTable[i];
|
|
1231
|
+
const selectQuery = generateSelectQuery(table, selects);
|
|
1232
|
+
try {
|
|
1233
|
+
const { orderBy, query } = convertRequestToQuery(
|
|
1234
|
+
request,
|
|
1235
|
+
tables,
|
|
1236
|
+
table,
|
|
1237
|
+
joinFromSelect,
|
|
1238
|
+
);
|
|
1239
|
+
unionBuilder += `${unionBuilder.length > 0 ? " UNION ALL " : ""} ${selectQuery} ${query}`;
|
|
1240
|
+
orderByClause =
|
|
1241
|
+
orderBy?.length > 0
|
|
1242
|
+
? orderByClause.length > 0
|
|
1243
|
+
? orderByClause + ", " + orderBy
|
|
1244
|
+
: orderBy
|
|
1245
|
+
: orderByClause;
|
|
1246
|
+
matchedOnce = true;
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
if (error instanceof MissingFieldError) {
|
|
1249
|
+
lastError = error;
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
throw error;
|
|
1253
|
+
}
|
|
1155
1254
|
}
|
|
1156
1255
|
|
|
1157
|
-
|
|
1256
|
+
if (!matchedOnce) {
|
|
1257
|
+
throw lastError;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
return `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} limit ? offset ?`;
|
|
1158
1261
|
};
|
|
1159
1262
|
|
|
1160
1263
|
type SearchQueryParts = { query: string; orderBy: string };
|
|
@@ -1197,9 +1300,7 @@ const convertRequestToQuery = <
|
|
|
1197
1300
|
|
|
1198
1301
|
if (request instanceof types.SearchRequest) {
|
|
1199
1302
|
if (request.sort.length > 0) {
|
|
1200
|
-
|
|
1201
|
-
orderByBuilder = "ORDER BY ";
|
|
1202
|
-
}
|
|
1303
|
+
orderByBuilder = "";
|
|
1203
1304
|
let once = false;
|
|
1204
1305
|
for (const sort of request.sort) {
|
|
1205
1306
|
const { foreignTables, queryKey } = resolveTableToQuery(
|
|
@@ -1403,10 +1504,7 @@ const resolveTableToQuery = (
|
|
|
1403
1504
|
// this means we need to also check if the key is a field in the current table
|
|
1404
1505
|
|
|
1405
1506
|
if (searchSelf) {
|
|
1406
|
-
const inlineName = getInlineTableFieldName(
|
|
1407
|
-
path.slice(0, -1),
|
|
1408
|
-
path[path.length - 1],
|
|
1409
|
-
);
|
|
1507
|
+
const inlineName = getInlineTableFieldName(path);
|
|
1410
1508
|
let field = table.fields.find((x) => x.name === inlineName);
|
|
1411
1509
|
if (field) {
|
|
1412
1510
|
return {
|
|
@@ -1428,7 +1526,7 @@ const resolveTableToQuery = (
|
|
|
1428
1526
|
const field = schema.fields.find((x) => x.key === key)!;
|
|
1429
1527
|
if (!field && currentTable.children.length > 0) {
|
|
1430
1528
|
// second arg is needed because of polymorphic fields we might end up here intentially to check what tables to query
|
|
1431
|
-
throw new
|
|
1529
|
+
throw new MissingFieldError(
|
|
1432
1530
|
`Property with key "${key}" is not found in the schema ${JSON.stringify(schema.fields.map((x) => x.key))}`,
|
|
1433
1531
|
);
|
|
1434
1532
|
}
|
|
@@ -1482,6 +1580,9 @@ const resolveTableToQuery = (
|
|
|
1482
1580
|
let foreignTables: JoinTable[] = currentTables.filter((x) =>
|
|
1483
1581
|
x.table.fields.find((x) => x.key === path[path.length - 1]),
|
|
1484
1582
|
);
|
|
1583
|
+
if (foreignTables.length === 0) {
|
|
1584
|
+
throw new MissingFieldError("Failed to find field to join");
|
|
1585
|
+
}
|
|
1485
1586
|
let tableToQuery: Table | undefined =
|
|
1486
1587
|
foreignTables[foreignTables.length - 1].table;
|
|
1487
1588
|
let queryKeyPath = [path[path.length - 1]];
|
|
@@ -1494,10 +1595,7 @@ const resolveTableToQuery = (
|
|
|
1494
1595
|
|
|
1495
1596
|
let queryKey =
|
|
1496
1597
|
queryKeyPath.length > 0
|
|
1497
|
-
? getInlineTableFieldName(
|
|
1498
|
-
queryKeyPath.slice(0, -1),
|
|
1499
|
-
queryKeyPath[queryKeyPath.length - 1],
|
|
1500
|
-
)
|
|
1598
|
+
? getInlineTableFieldName(queryKeyPath)
|
|
1501
1599
|
: FOREIGN_VALUE_PROPERTY;
|
|
1502
1600
|
return { queryKey, foreignTables };
|
|
1503
1601
|
};
|
|
@@ -1511,10 +1609,7 @@ const convertStateFieldQuery = (
|
|
|
1511
1609
|
tableAlias: string | undefined = undefined,
|
|
1512
1610
|
): { where: string } => {
|
|
1513
1611
|
// if field id represented as foreign table, do join and compare
|
|
1514
|
-
const inlinedName = getInlineTableFieldName(
|
|
1515
|
-
query.key.slice(0, query.key.length - 1),
|
|
1516
|
-
query.key[query.key.length - 1],
|
|
1517
|
-
);
|
|
1612
|
+
const inlinedName = getInlineTableFieldName(query.key);
|
|
1518
1613
|
const tableField = table.fields.find(
|
|
1519
1614
|
(x) => x.name === inlinedName,
|
|
1520
1615
|
); /* stringArraysEquals(query.key, [...table.parentPath, x.name]) )*/
|