@peerbit/indexer-sqlite3 1.0.7 → 1.1.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/src/schema.ts CHANGED
@@ -823,15 +823,25 @@ export const getTableNameFromPrefixedField = (prefixedField: string) =>
823
823
  prefixedField.split("#")[0];
824
824
 
825
825
  export const getInlineTableFieldName = (
826
- path: string[] | undefined,
826
+ path: string[] | string | undefined,
827
827
  key?: string,
828
- ) => {
828
+ ): string => {
829
829
  if (key) {
830
- return path && path.length > 0 ? `${path.join("_")}__${key}` : key;
830
+ if (Array.isArray(path)) {
831
+ return path && path.length > 0 ? `${path.join("_")}__${key}` : key;
832
+ }
833
+ return path + "__" + key;
831
834
  } else {
832
835
  // last element in the path is the key, the rest is the path
833
836
  // join key with __ , rest with _
834
837
 
838
+ if (!Array.isArray(path)) {
839
+ if (!path) {
840
+ throw new Error("Unexpected missing path");
841
+ }
842
+ return path;
843
+ }
844
+
835
845
  return path!.length > 2
836
846
  ? `${path!.slice(0, -1).join("_")}__${path![path!.length - 1]}`
837
847
  : path!.join("__");
@@ -840,7 +850,7 @@ export const getInlineTableFieldName = (
840
850
 
841
851
  const matchFieldInShape = (
842
852
  shape: types.Shape | undefined,
843
- path: string[] | undefined,
853
+ path: string[] | string | undefined,
844
854
  field: SQLField,
845
855
  ) => {
846
856
  if (!shape) {
@@ -860,7 +870,11 @@ const matchFieldInShape = (
860
870
  if (nextShape === true) {
861
871
  return true;
862
872
  }
863
- currentShape = nextShape;
873
+ if (Array.isArray(nextShape)) {
874
+ currentShape = nextShape[0];
875
+ } else {
876
+ currentShape = nextShape;
877
+ }
864
878
  }
865
879
  }
866
880
 
@@ -969,7 +983,12 @@ export const selectAllFieldsFromTable = (
969
983
  continue;
970
984
  }
971
985
 
972
- childShape = maybeShape === true ? undefined : maybeShape;
986
+ childShape =
987
+ maybeShape === true
988
+ ? undefined
989
+ : Array.isArray(maybeShape)
990
+ ? maybeShape[0]
991
+ : maybeShape;
973
992
  }
974
993
 
975
994
  stack.push({ table: child, shape: childShape });
@@ -1001,14 +1020,17 @@ const getNonInlinedTable = (from: Table) => {
1001
1020
  };
1002
1021
 
1003
1022
  // the inverse of resolveFieldValues
1004
- export const resolveInstanceFromValue = async <T>(
1023
+ export const resolveInstanceFromValue = async <
1024
+ T,
1025
+ S extends types.Shape | undefined,
1026
+ >(
1005
1027
  fromTablePrefixedValues: Record<string, any>,
1006
1028
  tables: Map<string, Table>,
1007
1029
  table: Table,
1008
1030
  resolveChildren: (parentId: any, table: Table) => Promise<any[]>,
1009
1031
  tablePrefixed: boolean,
1010
- shape: types.Shape | undefined,
1011
- ): Promise<T> => {
1032
+ shape?: S,
1033
+ ): Promise<types.ReturnTypeFromShape<T, S>> => {
1012
1034
  const fields = getSchema(table.ctor).fields;
1013
1035
  const obj: any = {};
1014
1036
 
@@ -1020,7 +1042,23 @@ export const resolveInstanceFromValue = async <T>(
1020
1042
  const subTables = getTableFromField(table, tables, field); // TODO fix
1021
1043
 
1022
1044
  let maybeShape = shape?.[field.key];
1023
- let subshape = maybeShape === true ? undefined : maybeShape;
1045
+ const subshapeIsArray = Array.isArray(maybeShape);
1046
+
1047
+ if (isArray && maybeShape && !subshapeIsArray && maybeShape !== true) {
1048
+ throw new Error(
1049
+ "Shape is not matching the array field type: " +
1050
+ field.key +
1051
+ ". Shape: " +
1052
+ JSON.stringify(shape),
1053
+ );
1054
+ }
1055
+
1056
+ let subshape =
1057
+ maybeShape === true
1058
+ ? undefined
1059
+ : subshapeIsArray
1060
+ ? maybeShape[0]
1061
+ : maybeShape;
1024
1062
 
1025
1063
  if (isArray) {
1026
1064
  let once = false;
@@ -1189,35 +1227,62 @@ export const fromRowToObj = (row: any, ctor: Constructor<any>) => {
1189
1227
  };
1190
1228
 
1191
1229
  export const convertDeleteRequestToQuery = (
1192
- request: types.DeleteRequest,
1230
+ request: types.DeleteOptions,
1193
1231
  tables: Map<string, Table>,
1194
1232
  table: Table,
1195
- ) => {
1196
- return `DELETE FROM ${table.name} WHERE ${table.primary} IN (SELECT ${table.primary} from ${table.name} ${convertRequestToQuery(request, tables, table).query}) returning ${table.primary}`;
1233
+ ): { sql: string; bindable: any[] } => {
1234
+ const { query, bindable } = convertRequestToQuery(
1235
+ "delete",
1236
+ request,
1237
+ tables,
1238
+ table,
1239
+ );
1240
+ return {
1241
+ sql: `DELETE FROM ${table.name} WHERE ${table.primary} IN (SELECT ${table.primary} from ${table.name} ${query}) returning ${table.primary}`,
1242
+ bindable,
1243
+ };
1197
1244
  };
1198
1245
 
1199
1246
  export const convertSumRequestToQuery = (
1200
- request: types.SumRequest,
1247
+ request: types.SumOptions,
1201
1248
  tables: Map<string, Table>,
1202
1249
  table: Table,
1203
- ) => {
1204
- return `SELECT SUM(${table.name}.${getInlineTableFieldName(request.key)}) as sum FROM ${table.name} ${convertRequestToQuery(request, tables, table).query}`;
1250
+ ): { sql: string; bindable: any[] } => {
1251
+ const { query, bindable } = convertRequestToQuery(
1252
+ "sum",
1253
+ request,
1254
+ tables,
1255
+ table,
1256
+ );
1257
+ return {
1258
+ sql: `SELECT SUM(${table.name}.${getInlineTableFieldName(request.key)}) as sum FROM ${table.name} ${query}`,
1259
+ bindable,
1260
+ };
1205
1261
  };
1206
1262
 
1207
1263
  export const convertCountRequestToQuery = (
1208
- request: types.CountRequest,
1264
+ request: types.CountOptions | undefined,
1209
1265
  tables: Map<string, Table>,
1210
1266
  table: Table,
1211
- ) => {
1212
- return `SELECT count(*) as count FROM ${table.name} ${convertRequestToQuery(request, tables, table).query}`;
1267
+ ): { sql: string; bindable: any[] } => {
1268
+ const { query, bindable } = convertRequestToQuery(
1269
+ "count",
1270
+ request,
1271
+ tables,
1272
+ table,
1273
+ );
1274
+ return {
1275
+ sql: `SELECT count(*) as count FROM ${table.name} ${query}`,
1276
+ bindable,
1277
+ };
1213
1278
  };
1214
1279
 
1215
1280
  export const convertSearchRequestToQuery = (
1216
- request: types.SearchRequest,
1281
+ request: types.IterateOptions | undefined,
1217
1282
  tables: Map<string, Table>,
1218
1283
  rootTables: Table[],
1219
1284
  shape: types.Shape | undefined,
1220
- ) => {
1285
+ ): { sql: string; bindable: any[] } => {
1221
1286
  let unionBuilder = "";
1222
1287
  let orderByClause: string = "";
1223
1288
 
@@ -1225,12 +1290,13 @@ export const convertSearchRequestToQuery = (
1225
1290
  let lastError: Error | undefined = undefined;
1226
1291
 
1227
1292
  const selectsPerTable = selectAllFieldsFromTables(rootTables, shape);
1228
-
1293
+ let bindableBuilder: any[] = [];
1229
1294
  for (const [i, table] of rootTables.entries()) {
1230
1295
  const { selects, joins: joinFromSelect } = selectsPerTable[i];
1231
1296
  const selectQuery = generateSelectQuery(table, selects);
1232
1297
  try {
1233
- const { orderBy, query } = convertRequestToQuery(
1298
+ const { orderBy, query, bindable } = convertRequestToQuery(
1299
+ "iterate",
1234
1300
  request,
1235
1301
  tables,
1236
1302
  table,
@@ -1244,6 +1310,7 @@ export const convertSearchRequestToQuery = (
1244
1310
  : orderBy
1245
1311
  : orderByClause;
1246
1312
  matchedOnce = true;
1313
+ bindableBuilder.push(...bindable);
1247
1314
  } catch (error) {
1248
1315
  if (error instanceof MissingFieldError) {
1249
1316
  lastError = error;
@@ -1254,78 +1321,103 @@ export const convertSearchRequestToQuery = (
1254
1321
  }
1255
1322
 
1256
1323
  if (!matchedOnce) {
1257
- throw lastError;
1324
+ throw lastError!;
1258
1325
  }
1259
1326
 
1260
- return `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} limit ? offset ?`;
1327
+ return {
1328
+ sql: `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} limit ? offset ?`,
1329
+ bindable: bindableBuilder,
1330
+ };
1261
1331
  };
1262
1332
 
1263
- type SearchQueryParts = { query: string; orderBy: string };
1264
- type CountQueryParts = { query: string; join: string };
1333
+ type SearchQueryParts = { query: string; orderBy: string; bindable: any[] };
1334
+ type CountQueryParts = { query: string; join: string; bindable: any[] };
1335
+
1336
+ function isIterateRequest(
1337
+ request: any,
1338
+ type: string,
1339
+ ): request is types.IterateOptions | undefined {
1340
+ return type === "iterate";
1341
+ }
1265
1342
 
1266
1343
  const convertRequestToQuery = <
1267
- T extends types.SearchRequest | types.CountRequest | types.SumRequest,
1268
- R = T extends types.SearchRequest ? SearchQueryParts : CountQueryParts,
1344
+ T extends "iterate" | "count" | "sum" | "delete",
1345
+ R = T extends "iterate" ? SearchQueryParts : CountQueryParts,
1269
1346
  >(
1270
- request: T,
1347
+ type: T,
1348
+ request:
1349
+ | (T extends "iterate"
1350
+ ? types.IterateOptions
1351
+ : T extends "count"
1352
+ ? types.CountOptions
1353
+ : T extends "delete"
1354
+ ? types.DeleteOptions
1355
+ : types.SumOptions)
1356
+ | undefined,
1271
1357
  tables: Map<string, Table>,
1272
1358
  table: Table,
1273
1359
  extraJoin?: Map<string, JoinTable>,
1274
1360
  path: string[] = [],
1275
1361
  ): R => {
1276
1362
  let whereBuilder = "";
1363
+ let bindableBuilder: any[] = [];
1277
1364
  let orderByBuilder: string | undefined = undefined;
1278
1365
  /* let tablesToSelect: string[] = [table.name]; */
1279
1366
  let joinBuilder: Map<string, JoinTable> = extraJoin || new Map();
1280
-
1281
- if (request.query.length === 1) {
1282
- const { where } = convertQueryToSQLQuery(
1283
- request.query[0],
1367
+ const coercedQuery = types.toQuery(request?.query);
1368
+ if (coercedQuery.length === 1) {
1369
+ const { where, bindable } = convertQueryToSQLQuery(
1370
+ coercedQuery[0],
1284
1371
  tables,
1285
1372
  table,
1286
1373
  joinBuilder,
1287
1374
  path,
1288
1375
  );
1289
1376
  whereBuilder += where;
1290
- } else if (request.query.length > 1) {
1291
- const { where } = convertQueryToSQLQuery(
1292
- new types.And(request.query),
1377
+ bindableBuilder.push(...bindable);
1378
+ } else if (coercedQuery.length > 1) {
1379
+ const { where, bindable } = convertQueryToSQLQuery(
1380
+ new types.And(coercedQuery),
1293
1381
  tables,
1294
1382
  table,
1295
1383
  joinBuilder,
1296
1384
  path,
1297
1385
  );
1298
1386
  whereBuilder += where;
1387
+ bindableBuilder.push(...bindable);
1299
1388
  }
1300
1389
 
1301
- if (request instanceof types.SearchRequest) {
1302
- if (request.sort.length > 0) {
1303
- orderByBuilder = "";
1304
- let once = false;
1305
- for (const sort of request.sort) {
1306
- const { foreignTables, queryKey } = resolveTableToQuery(
1307
- table,
1308
- tables,
1309
- joinBuilder,
1310
- [...path, ...sort.key],
1311
- undefined,
1312
- true,
1313
- );
1314
- for (const table of foreignTables) {
1315
- if (once) {
1316
- orderByBuilder += ", ";
1390
+ if (isIterateRequest(request, type)) {
1391
+ if (request?.sort) {
1392
+ let sortArr = Array.isArray(request.sort) ? request.sort : [request.sort];
1393
+ if (sortArr.length > 0) {
1394
+ orderByBuilder = "";
1395
+ let once = false;
1396
+ for (const sort of sortArr) {
1397
+ const { foreignTables, queryKey } = resolveTableToQuery(
1398
+ table,
1399
+ tables,
1400
+ joinBuilder,
1401
+ [...path, ...sort.key],
1402
+ undefined,
1403
+ true,
1404
+ );
1405
+ for (const table of foreignTables) {
1406
+ if (once) {
1407
+ orderByBuilder += ", ";
1408
+ }
1409
+ once = true;
1410
+ orderByBuilder += `${table.as}.${queryKey} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`;
1317
1411
  }
1318
- once = true;
1319
- orderByBuilder += `${table.as}.${queryKey} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`;
1320
1412
  }
1321
- }
1322
1413
 
1323
- /* orderByBuilder += request.sort
1324
- .map(
1325
- (sort) =>
1326
- `${table.name}.${sort.key} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`
1327
- )
1328
- .join(", "); */
1414
+ /* orderByBuilder += request.sort
1415
+ .map(
1416
+ (sort) =>
1417
+ `${table.name}.${sort.key} ${sort.direction === types.SortDirection.ASC ? "ASC" : "DESC"}`
1418
+ )
1419
+ .join(", "); */
1420
+ }
1329
1421
  }
1330
1422
  }
1331
1423
  const where = whereBuilder.length > 0 ? "where " + whereBuilder : undefined;
@@ -1333,16 +1425,14 @@ const convertRequestToQuery = <
1333
1425
  if (extraJoin && extraJoin.size > 0) {
1334
1426
  insertMapIntoMap(joinBuilder, extraJoin);
1335
1427
  }
1336
- let join = buildJoin(
1337
- joinBuilder,
1338
- request instanceof types.SearchRequest ? true : false,
1339
- );
1428
+ let join = buildJoin(joinBuilder, type === "iterate" ? true : false);
1340
1429
 
1341
1430
  const query = `${join ? join : ""} ${where ? where : ""}`;
1342
1431
 
1343
1432
  return {
1344
1433
  query,
1345
1434
  orderBy: orderByBuilder,
1435
+ bindable: bindableBuilder,
1346
1436
  } as R;
1347
1437
  };
1348
1438
 
@@ -1382,8 +1472,9 @@ export const convertQueryToSQLQuery = (
1382
1472
  joinBuilder: Map<string, JoinTable>,
1383
1473
  path: string[] = [],
1384
1474
  tableAlias: string | undefined = undefined,
1385
- ): { where: string } => {
1475
+ ): { where: string; bindable: any[] } => {
1386
1476
  let whereBuilder = "";
1477
+ let bindableBuilder: any[] = [];
1387
1478
  /* let tablesToSelect: string[] = []; */
1388
1479
 
1389
1480
  const handleAnd = (
@@ -1392,7 +1483,7 @@ export const convertQueryToSQLQuery = (
1392
1483
  tableAlias?: string,
1393
1484
  ) => {
1394
1485
  for (const query of queries) {
1395
- const { where } = convertQueryToSQLQuery(
1486
+ const { where, bindable } = convertQueryToSQLQuery(
1396
1487
  query,
1397
1488
  tables,
1398
1489
  table,
@@ -1402,11 +1493,12 @@ export const convertQueryToSQLQuery = (
1402
1493
  );
1403
1494
  whereBuilder =
1404
1495
  whereBuilder.length > 0 ? `(${whereBuilder}) AND (${where})` : where;
1496
+ bindableBuilder.push(...bindable);
1405
1497
  }
1406
1498
  };
1407
1499
 
1408
1500
  if (query instanceof types.StateFieldQuery) {
1409
- const { where } = convertStateFieldQuery(
1501
+ const { where, bindable } = convertStateFieldQuery(
1410
1502
  query,
1411
1503
  tables,
1412
1504
  table,
@@ -1415,6 +1507,7 @@ export const convertQueryToSQLQuery = (
1415
1507
  tableAlias,
1416
1508
  );
1417
1509
  whereBuilder += where;
1510
+ bindableBuilder.push(...bindable);
1418
1511
  } else if (query instanceof types.Nested) {
1419
1512
  let joinPrefix = "__" + String(tables.size);
1420
1513
  path = [...path, query.path];
@@ -1424,7 +1517,7 @@ export const convertQueryToSQLQuery = (
1424
1517
  handleAnd(query.and, path, tableAlias);
1425
1518
  } else if (query instanceof types.Or) {
1426
1519
  for (const subquery of query.or) {
1427
- const { where } = convertQueryToSQLQuery(
1520
+ const { where, bindable } = convertQueryToSQLQuery(
1428
1521
  subquery,
1429
1522
  tables,
1430
1523
  table,
@@ -1434,9 +1527,10 @@ export const convertQueryToSQLQuery = (
1434
1527
  );
1435
1528
  whereBuilder =
1436
1529
  whereBuilder.length > 0 ? `(${whereBuilder}) OR (${where})` : where;
1530
+ bindableBuilder.push(...bindable);
1437
1531
  }
1438
1532
  } else if (query instanceof types.Not) {
1439
- const { where } = convertQueryToSQLQuery(
1533
+ const { where, bindable } = convertQueryToSQLQuery(
1440
1534
  query.not,
1441
1535
  tables,
1442
1536
  table,
@@ -1445,6 +1539,7 @@ export const convertQueryToSQLQuery = (
1445
1539
  tableAlias,
1446
1540
  );
1447
1541
  whereBuilder = `NOT (${where})`;
1542
+ bindableBuilder.push(...bindable);
1448
1543
  } else {
1449
1544
  throw new Error("Unsupported query type: " + query.constructor.name);
1450
1545
  }
@@ -1454,6 +1549,7 @@ export const convertQueryToSQLQuery = (
1454
1549
 
1455
1550
  return {
1456
1551
  where: whereBuilder,
1552
+ bindable: bindableBuilder,
1457
1553
  };
1458
1554
  };
1459
1555
 
@@ -1607,7 +1703,7 @@ const convertStateFieldQuery = (
1607
1703
  join: Map<string, JoinTable>,
1608
1704
  path: string[],
1609
1705
  tableAlias: string | undefined = undefined,
1610
- ): { where: string } => {
1706
+ ): { where: string; bindable: any[] } => {
1611
1707
  // if field id represented as foreign table, do join and compare
1612
1708
  const inlinedName = getInlineTableFieldName(query.key);
1613
1709
  const tableField = table.fields.find(
@@ -1626,11 +1722,13 @@ const convertStateFieldQuery = (
1626
1722
  query = cloneQuery(query);
1627
1723
  query.key = [queryKey];
1628
1724
  let whereBuilder: string[] = [];
1725
+ let bindableBuilder: any[][] = [];
1726
+
1629
1727
  for (const ftable of foreignTables) {
1630
1728
  if (ftable.table === table) {
1631
1729
  throw new Error("Unexpected");
1632
1730
  }
1633
- const { where } = convertQueryToSQLQuery(
1731
+ const { where, bindable } = convertQueryToSQLQuery(
1634
1732
  query,
1635
1733
  tables,
1636
1734
  ftable.table,
@@ -1639,10 +1737,15 @@ const convertStateFieldQuery = (
1639
1737
  ftable.as,
1640
1738
  );
1641
1739
  whereBuilder.push(where);
1740
+ bindableBuilder.push(bindable);
1642
1741
  }
1643
- return { where: whereBuilder.join(" OR ") };
1742
+ return {
1743
+ where: whereBuilder.join(" OR "),
1744
+ bindable: bindableBuilder.flat(),
1745
+ };
1644
1746
  }
1645
1747
 
1748
+ let bindable: any[] = [];
1646
1749
  const keyWithTable =
1647
1750
  (tableAlias || table.name) + "." + escapeColumnName(inlinedName);
1648
1751
  let where: string;
@@ -1650,11 +1753,14 @@ const convertStateFieldQuery = (
1650
1753
  let statement = "";
1651
1754
 
1652
1755
  if (query.method === types.StringMatchMethod.contains) {
1653
- statement = `${keyWithTable} LIKE '%${query.value}%'`;
1756
+ statement = `${keyWithTable} LIKE ?`;
1757
+ bindable.push(`%${query.value}%`);
1654
1758
  } else if (query.method === types.StringMatchMethod.prefix) {
1655
- statement = `${keyWithTable} LIKE '${query.value}%'`;
1759
+ statement = `${keyWithTable} LIKE ?`;
1760
+ bindable.push(`${query.value}%`);
1656
1761
  } else if (query.method === types.StringMatchMethod.exact) {
1657
- statement = `${keyWithTable} = '${query.value}'`;
1762
+ statement = `${keyWithTable} = ?`;
1763
+ bindable.push(`${query.value}`);
1658
1764
  }
1659
1765
  if (query.caseInsensitive) {
1660
1766
  statement += " COLLATE NOCASE";
@@ -1663,31 +1769,39 @@ const convertStateFieldQuery = (
1663
1769
  } else if (query instanceof types.ByteMatchQuery) {
1664
1770
  // compare Blob compule with f.value
1665
1771
 
1666
- const statement = `${keyWithTable} = x'${toHexString(query.value)}'`;
1772
+ const statement = `${keyWithTable} = ?`;
1773
+ bindable.push(query.value);
1667
1774
  where = statement;
1668
1775
  } else if (query instanceof types.IntegerCompare) {
1669
1776
  if (tableField!.type === "BLOB") {
1670
1777
  // TODO perf
1671
- where = `hex(${keyWithTable}) LIKE '%${toHexString(new Uint8Array([Number(query.value.value)]))}%'`;
1672
- } else if (query.compare === types.Compare.Equal) {
1673
- where = `${keyWithTable} = ${query.value.value}`;
1674
- } else if (query.compare === types.Compare.Greater) {
1675
- where = `${keyWithTable} > ${query.value.value}`;
1676
- } else if (query.compare === types.Compare.Less) {
1677
- where = `${keyWithTable} < ${query.value.value}`;
1678
- } else if (query.compare === types.Compare.GreaterOrEqual) {
1679
- where = `${keyWithTable} >= ${query.value.value}`;
1680
- } else if (query.compare === types.Compare.LessOrEqual) {
1681
- where = `${keyWithTable} <= ${query.value.value}`;
1778
+ where = `hex(${keyWithTable}) LIKE ?`;
1779
+ bindable.push(
1780
+ `%${toHexString(new Uint8Array([Number(query.value.value)]))}%`,
1781
+ );
1682
1782
  } else {
1683
- throw new Error(`Unsupported compare type: ${query.compare}`);
1783
+ if (query.compare === types.Compare.Equal) {
1784
+ where = `${keyWithTable} = ?`;
1785
+ } else if (query.compare === types.Compare.Greater) {
1786
+ where = `${keyWithTable} > ?`;
1787
+ } else if (query.compare === types.Compare.Less) {
1788
+ where = `${keyWithTable} < ?`;
1789
+ } else if (query.compare === types.Compare.GreaterOrEqual) {
1790
+ where = `${keyWithTable} >= ?`;
1791
+ } else if (query.compare === types.Compare.LessOrEqual) {
1792
+ where = `${keyWithTable} <= ?`;
1793
+ } else {
1794
+ throw new Error(`Unsupported compare type: ${query.compare}`);
1795
+ }
1796
+ bindable.push(query.value.value);
1684
1797
  }
1685
1798
  } else if (query instanceof types.IsNull) {
1686
1799
  where = `${keyWithTable} IS NULL`;
1687
1800
  } else if (query instanceof types.BoolQuery) {
1688
- where = `${keyWithTable} = ${query.value}`;
1801
+ where = `${keyWithTable} = ?`;
1802
+ bindable.push(query.value ? 1 : 0);
1689
1803
  } else {
1690
1804
  throw new Error("Unsupported query type: " + query.constructor.name);
1691
1805
  }
1692
- return { where };
1806
+ return { where, bindable };
1693
1807
  };
@@ -93,13 +93,14 @@ class ProxyStatement implements IStatement {
93
93
 
94
94
  class ProxyDatabase implements IDatabase {
95
95
  statements: Map<string, ProxyStatement> = new Map();
96
+
96
97
  resolvers: {
97
98
  [hash in string]: {
98
99
  resolve: (...args: any) => void;
99
100
  reject: (...args: any) => void;
100
101
  };
101
102
  } = {};
102
- databaseId: string;
103
+ databaseId!: string;
103
104
  constructor(
104
105
  readonly send: <T>(
105
106
  message: messages.DatabaseMessages | messages.StatementMessages,
@@ -125,7 +126,14 @@ class ProxyDatabase implements IDatabase {
125
126
  });
126
127
  }
127
128
 
128
- async prepare(sql: string) {
129
+ async prepare(sql: string, id?: string) {
130
+ if (id != null) {
131
+ const prev = this.statements.get(id);
132
+ if (prev) {
133
+ await prev.reset();
134
+ return prev;
135
+ }
136
+ }
129
137
  const statementId = await this.send<string>({
130
138
  type: "prepare",
131
139
  sql,
@@ -138,6 +146,11 @@ class ProxyDatabase implements IDatabase {
138
146
  statementId,
139
147
  );
140
148
  this.statements.set(statementId, statement);
149
+
150
+ if (id != null) {
151
+ this.statements.set(id, statement);
152
+ }
153
+
141
154
  return statement;
142
155
  }
143
156
 
package/src/sqlite3.ts CHANGED
@@ -7,7 +7,14 @@ import type {
7
7
 
8
8
  let create = async (directory?: string) => {
9
9
  let db: DB.Database | undefined = undefined;
10
+ let statements: Map<string, IStatement> = new Map();
11
+
10
12
  let close = () => {
13
+ for (const stmt of statements.values()) {
14
+ stmt.finalize?.();
15
+ }
16
+ statements.clear();
17
+
11
18
  if (db) {
12
19
  db.close();
13
20
  db = undefined;
@@ -43,10 +50,23 @@ let create = async (directory?: string) => {
43
50
  if (!db) throw new Error("Database not open");
44
51
  return db.exec(sql);
45
52
  },
46
- prepare(sql: string) {
53
+ async prepare(sql: string, id?: string) {
47
54
  if (!db) throw new Error("Database not open");
48
- return db.prepare(sql) as any as IStatement; // TODO types
55
+ if (id != null) {
56
+ let prev = statements.get(id);
57
+
58
+ if (prev) {
59
+ await prev.reset?.();
60
+ return prev;
61
+ }
62
+ }
63
+ const stmt = db.prepare(sql) as any as IStatement; // TODO types
64
+ if (id != null) {
65
+ statements.set(id, stmt);
66
+ }
67
+ return stmt;
49
68
  },
69
+ statements,
50
70
  close,
51
71
  open,
52
72
  status: () => (db ? "open" : "closed"),