@livestore/wa-sqlite 1.0.1-dev.9 → 1.0.3-dev.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.
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/wa-sqlite",
3
- "version": "1.0.1-dev.9",
3
+ "version": "1.0.3-dev.3",
4
4
  "type": "module",
5
5
  "main": "src/sqlite-api.js",
6
6
  "types": "src/types/index.d.ts",
@@ -40,6 +40,5 @@
40
40
  "web-test-runner-jasmine@0.0.6": {
41
41
  "unplugged": true
42
42
  }
43
- },
44
- "packageManager": "yarn@4.0.2"
45
- }
43
+ }
44
+ }
package/src/sqlite-api.js CHANGED
@@ -249,16 +249,46 @@ export function Factory(Module) {
249
249
  };
250
250
  })();
251
251
 
252
+ const SQLITE_SERIALIZE_NOCOPY = 0x0_01
253
+
252
254
  sqlite3.serialize = (function() {
253
255
  const fname = 'sqlite3_serialize';
254
256
  const f = Module.cwrap(fname, ...decl('nsnn:n'));
255
- return function(db, schema, piSize, flags) {
257
+ return function(db, schema) {
256
258
  verifyDatabase(db);
257
- // TODO alloc + free memory for result?
258
- const address = f(db, schema, piSize, flags);
259
- const result = Module.HEAPU8.subarray(address, address + piSize);
259
+ const piSize = tmpPtr[0];
260
+ let address = f(db, schema, piSize, 0);
261
+ if (address === 0) {
262
+ address = f(db, schema, piSize, SQLITE_SERIALIZE_NOCOPY);
263
+ const size = Module.getValue(piSize, '*');
264
+ const result = Module.HEAPU8.subarray(address, address + size);
265
+ // NOTE Given that the memory is owned by SQLite, we must copy it.
266
+ return new Uint8Array(result.slice());
267
+ } else {
268
+ const size = Module.getValue(piSize, '*');
269
+ const result = Module.HEAPU8.subarray(address, address + size);
270
+ // Here we're getting a copy of the memory, so we can return it directly.
271
+ return new Uint8Array(result);
272
+ }
273
+ };
274
+ })();
260
275
 
261
- return result;
276
+ // https://www.sqlite.org/c3ref/backup_finish.html
277
+ // https://www.sqlite.org/backup.html
278
+ sqlite3.backup = (function() {
279
+ const fInit = Module.cwrap('sqlite3_backup_init', ...decl('nsns:n'));
280
+ const fStep = Module.cwrap('sqlite3_backup_step', ...decl('nn:n'));
281
+ const fFinish = Module.cwrap('sqlite3_backup_finish', ...decl('n:n'));
282
+ return function(dest, destName, source, sourceName) {
283
+ verifyDatabase(dest);
284
+ verifyDatabase(source);
285
+ const backup = fInit(dest, destName, source, sourceName);
286
+ if (backup === 0) {
287
+ const errMsg = Module.ccall('sqlite3_errmsg', 'string', ['number'], [dest]);
288
+ throw new SQLiteError(`backup failed: ${errMsg}`, SQLite.SQLITE_ERROR);
289
+ }
290
+ fStep(backup, -1);
291
+ return fFinish(backup);
262
292
  };
263
293
  })();
264
294
 
@@ -458,7 +488,8 @@ export function Factory(Module) {
458
488
  // return SQLite.SQLITE_OK;
459
489
  // };
460
490
  sqlite3.exec = function(db, sql, callback) {
461
- for (const stmt of sqlite3.statements(db, sql)) {
491
+ const stmts = sqlite3.statements(db, sql, { unscoped: true });
492
+ for (const stmt of stmts) {
462
493
  let columns;
463
494
  while (sqlite3.step(stmt) === SQLite.SQLITE_ROW) {
464
495
  if (callback) {
@@ -468,6 +499,9 @@ export function Factory(Module) {
468
499
  }
469
500
  }
470
501
  }
502
+ for (const stmt of stmts) {
503
+ sqlite3.finalize(stmt);
504
+ }
471
505
  return SQLite.SQLITE_OK;
472
506
  };
473
507
 
@@ -523,6 +557,28 @@ export function Factory(Module) {
523
557
  })();
524
558
 
525
559
  sqlite3.open_v2 = (function() {
560
+ const fname = 'sqlite3_open_v2';
561
+ const f = Module.cwrap(fname, ...decl('snnn:n'), { async });
562
+ return async function(zFilename, flags, zVfs) {
563
+ flags = flags || SQLite.SQLITE_OPEN_CREATE | SQLite.SQLITE_OPEN_READWRITE;
564
+ zVfs = createUTF8(zVfs);
565
+ try {
566
+ // Allow retry operations.
567
+ const rc = await retry(() => f(zFilename, tmpPtr[0], flags, zVfs));
568
+
569
+ const db = Module.getValue(tmpPtr[0], '*');
570
+ databases.add(db);
571
+
572
+ Module.ccall('RegisterExtensionFunctions', 'void', ['number'], [db]);
573
+ check(fname, rc, db);
574
+ return db;
575
+ } finally {
576
+ Module._sqlite3_free(zVfs);
577
+ }
578
+ };
579
+ })();
580
+
581
+ sqlite3.open_v2Sync = (function() {
526
582
  const fname = 'sqlite3_open_v2';
527
583
  const f = Module.cwrap(fname, ...decl('snnn:n'), { async });
528
584
  return function(zFilename, flags, zVfs) {
@@ -537,7 +593,7 @@ export function Factory(Module) {
537
593
  databases.add(db);
538
594
 
539
595
  Module.ccall('RegisterExtensionFunctions', 'void', ['number'], [db]);
540
- check(fname, rc);
596
+ check(fname, rc, db);
541
597
  return db;
542
598
  } finally {
543
599
  Module._sqlite3_free(zVfs);
@@ -545,6 +601,7 @@ export function Factory(Module) {
545
601
  };
546
602
  })();
547
603
 
604
+
548
605
  sqlite3.progress_handler = function(db, nProgressOps, handler, userData) {
549
606
  verifyDatabase(db);
550
607
  Module.progress_handler(db, nProgressOps, handler, userData);
@@ -675,9 +732,9 @@ export function Factory(Module) {
675
732
  };
676
733
  function adapt(f) {
677
734
  // return f instanceof AsyncFunction ?
678
- // (async (_, iAction, p3, p4, p5, p6) => f(cvtArgs(_, iAction, p3, p4, p5, p6))) :
679
- // ((_, iAction, p3, p4, p5, p6) => f(cvtArgs(_, iAction, p3, p4, p5, p6)));
680
- return ((_, iAction, p3, p4, p5, p6) => f(cvtArgs(_, iAction, p3, p4, p5, p6)));
735
+ // (async (_, iAction, p3, p4, p5, p6) => f(...cvtArgs(_, iAction, p3, p4, p5, p6))) :
736
+ // ((_, iAction, p3, p4, p5, p6) => f(...cvtArgs(_, iAction, p3, p4, p5, p6)));
737
+ return ((_, iAction, p3, p4, p5, p6) => f(...cvtArgs(_, iAction, p3, p4, p5, p6)));
681
738
  }
682
739
 
683
740
  const result = Module.set_authorizer(db, adapt(xAuth), pApp);
@@ -702,10 +759,10 @@ export function Factory(Module) {
702
759
  // { async: true });
703
760
  { async: false });
704
761
 
705
- const results = [];
762
+ const stmts = [];
706
763
 
707
764
  const onFinally = [];
708
- try {
765
+ // try {
709
766
  // Encode SQL string to UTF-8.
710
767
  const utf8 = new TextEncoder().encode(sql);
711
768
 
@@ -767,16 +824,16 @@ export function Factory(Module) {
767
824
  if (stmt) {
768
825
  mapStmtToDB.set(stmt, db);
769
826
  // yield stmt;
770
- results.push(stmt);
827
+ stmts.push(stmt);
771
828
  }
772
829
  } while (stmt);
773
- } finally {
774
- while (onFinally.length) {
775
- onFinally.pop()();
776
- }
777
- }
830
+ // } finally {
831
+ // while (onFinally.length) {
832
+ // onFinally.pop()();
833
+ // }
834
+ // }
778
835
 
779
- return results;
836
+ return stmts;
780
837
  };
781
838
 
782
839
  sqlite3.step = (function() {
@@ -794,6 +851,318 @@ export function Factory(Module) {
794
851
  };
795
852
  })();
796
853
 
854
+ sqlite3.update_hook = function(db, xUpdateHook) {
855
+ verifyDatabase(db);
856
+
857
+ // Convert SQLite callback arguments to JavaScript-friendly arguments.
858
+ function cvtArgs(iUpdateType, dbName, tblName, lo32, hi32) {
859
+ return [
860
+ iUpdateType,
861
+ Module.UTF8ToString(dbName),
862
+ Module.UTF8ToString(tblName),
863
+ cvt32x2ToBigInt(lo32, hi32)
864
+ ];
865
+ };
866
+ function adapt(f) {
867
+ // return f instanceof AsyncFunction ?
868
+ // (async (iUpdateType, dbName, tblName, lo32, hi32) => f(...cvtArgs(iUpdateType, dbName, tblName, lo32, hi32))) :
869
+ // ((iUpdateType, dbName, tblName, lo32, hi32) => f(...cvtArgs(iUpdateType, dbName, tblName, lo32, hi32)));
870
+ return ((iUpdateType, dbName, tblName, lo32, hi32) => f(...cvtArgs(iUpdateType, dbName, tblName, lo32, hi32)));
871
+ }
872
+
873
+ Module.update_hook(db, adapt(xUpdateHook));
874
+ };;
875
+
876
+ // Session extension bindings
877
+ sqlite3.session_create = (function() {
878
+ const fname = 'sqlite3session_create';
879
+ const f = Module.cwrap(fname, ...decl('nsn:n'));
880
+ return function(db, zDb) {
881
+ verifyDatabase(db);
882
+ const ppSession = Module._malloc(4);
883
+ const result = f(db, zDb, ppSession);
884
+
885
+ if (result !== SQLite.SQLITE_OK) {
886
+ check(fname, result, db);
887
+ }
888
+
889
+ const pSession = Module.getValue(ppSession, 'i32');
890
+ return pSession;
891
+ };
892
+ })();
893
+
894
+ sqlite3.session_attach = (function() {
895
+ const fname = 'sqlite3session_attach';
896
+ const f = Module.cwrap(fname, ...decl('ns:n'));
897
+ return function(pSession, zTab) {
898
+ if (typeof pSession !== 'number') {
899
+ throw new SQLiteError('Invalid session object', SQLite.SQLITE_MISUSE);
900
+ }
901
+ const result = f(pSession, zTab);
902
+ return check(fname, result);
903
+ };
904
+ })();
905
+
906
+ sqlite3.session_enable = (function() {
907
+ const fname = 'sqlite3session_enable';
908
+ const f = Module.cwrap(fname, ...decl('nn:n'));
909
+ return function(pSession, enableBool) {
910
+ const enable = enableBool ? 1 : 0;
911
+ if (typeof pSession !== 'number') {
912
+ throw new SQLiteError('Invalid session object', SQLite.SQLITE_MISUSE);
913
+ }
914
+ const result = f(pSession, enable);
915
+ if (result !== enable) {
916
+ throw new SQLiteError('Failed to enable session', SQLite.SQLITE_MISUSE);
917
+ }
918
+ };
919
+ })();
920
+
921
+ sqlite3.session_changeset = (function() {
922
+ const fname = 'sqlite3session_changeset';
923
+ const f = Module.cwrap(fname, ...decl('nnn:n'));
924
+ return function(pSession) {
925
+ if (typeof pSession !== 'number') {
926
+ throw new SQLiteError('Invalid session object', SQLite.SQLITE_MISUSE);
927
+ }
928
+
929
+ // Allocate memory for the size (int) and the changeset pointer (void*)
930
+ const sizePtr = Module._malloc(4);
931
+ const changesetPtrPtr = Module._malloc(4);
932
+
933
+ try {
934
+ const result = f(pSession, sizePtr, changesetPtrPtr);
935
+ if (result === SQLite.SQLITE_OK) {
936
+ // Get the size of the changeset
937
+ const size = Module.getValue(sizePtr, 'i32');
938
+ // Get the pointer to the changeset
939
+ const changesetPtr = Module.getValue(changesetPtrPtr, 'i32');
940
+
941
+ // Ensure the pointer is valid before accessing memory
942
+ if (changesetPtr === 0) {
943
+ throw new SQLiteError('Failed to retrieve changeset pointer', SQLite.SQLITE_ERROR);
944
+ }
945
+
946
+ // Copy the changeset data
947
+ const changeset = new Uint8Array(Module.HEAPU8.subarray(changesetPtr, changesetPtr + size));
948
+
949
+ // Free the allocated changeset memory
950
+ Module._sqlite3_free(changesetPtr);
951
+
952
+ // Return a copy of the changeset
953
+ return {
954
+ result: result,
955
+ size: size,
956
+ changeset: changeset
957
+ };
958
+ }
959
+ return check(fname, result);
960
+ } finally {
961
+ // Free the allocated memory
962
+ Module._free(sizePtr);
963
+ Module._free(changesetPtrPtr);
964
+ }
965
+ };
966
+ })();
967
+
968
+ sqlite3.session_delete = (function() {
969
+ const fname = 'sqlite3session_delete';
970
+ const f = Module.cwrap(fname, ...decl('n:v'));
971
+ return function(pSession) {
972
+ if (typeof pSession !== 'number') {
973
+ throw new SQLiteError('Invalid session object', SQLite.SQLITE_MISUSE);
974
+ }
975
+ const result = f(pSession);
976
+ return result;
977
+ };
978
+ })();
979
+
980
+ sqlite3.changeset_start = (function() {
981
+ const fname = 'sqlite3changeset_start';
982
+ const f = Module.cwrap(fname, ...decl('nnn:n'));
983
+ return function(changesetData) {
984
+ // Allocate memory for the input changeset data
985
+ const inPtr = Module._sqlite3_malloc(changesetData.length);
986
+ Module.HEAPU8.subarray(inPtr).set(changesetData);
987
+
988
+ // Allocate memory for the changeset iterator pointer
989
+ const ppIter = Module._malloc(4);
990
+
991
+ try {
992
+ // Call the wrapped C function
993
+ const result = f(ppIter, changesetData.length, inPtr);
994
+
995
+ if (result !== SQLite.SQLITE_OK) {
996
+ check(fname, result); // Handle errors appropriately
997
+ }
998
+
999
+ // Retrieve the changeset iterator handle
1000
+ const pIter = Module.getValue(ppIter, 'i32');
1001
+
1002
+ return pIter;
1003
+ } finally {
1004
+ // Free allocated memory
1005
+ Module._sqlite3_free(inPtr);
1006
+ Module._free(ppIter);
1007
+ }
1008
+ };
1009
+ })();
1010
+
1011
+ sqlite3.changeset_finalize = (function() {
1012
+ const fname = 'sqlite3changeset_finalize';
1013
+ const f = Module.cwrap(fname, ...decl('n:n'));
1014
+ return function(pIter) {
1015
+ const result = f(pIter);
1016
+ return result;
1017
+ };
1018
+ })();
1019
+
1020
+ sqlite3.changeset_invert = (function() {
1021
+ const fname = 'sqlite3changeset_invert';
1022
+ const f = Module.cwrap(fname, ...decl('nn:nn'));
1023
+ return function(changesetData) {
1024
+ // Allocate memory for the input changeset data
1025
+ const inPtr = Module._sqlite3_malloc(changesetData.length);
1026
+ Module.HEAPU8.subarray(inPtr).set(changesetData);
1027
+
1028
+ // Allocate memory for the output changeset length and pointer
1029
+ const outLengthPtr = Module._malloc(4);
1030
+ const outPtrPtr = Module._malloc(4);
1031
+
1032
+ // Call the wrapped C function
1033
+ const result = f(changesetData.length, inPtr, outLengthPtr, outPtrPtr);
1034
+
1035
+ if (result !== SQLite.SQLITE_OK) {
1036
+ check(fname, result); // Handle errors appropriately
1037
+ }
1038
+
1039
+ // Retrieve the size and pointer of the inverted changeset
1040
+ const outLength = Module.getValue(outLengthPtr, 'i32');
1041
+ const changesetOutPtr = Module.getValue(outPtrPtr, 'i32');
1042
+
1043
+ // Copy the inverted changeset data
1044
+ const changesetOut = new Uint8Array(Module.HEAPU8.buffer, changesetOutPtr, outLength);
1045
+
1046
+ // Free allocated memory
1047
+ Module._sqlite3_free(inPtr);
1048
+ Module._sqlite3_free(outLengthPtr);
1049
+ Module._sqlite3_free(outPtrPtr);
1050
+
1051
+ return changesetOut;
1052
+ };
1053
+ })();
1054
+
1055
+ /**
1056
+ * Convenience function to get an inverted changeset from a session
1057
+ * without having to call sqlite3session_changeset() and then sqlite3changeset_invert().
1058
+ * It's more efficient as it's reusing the same memory allocation for the changeset.
1059
+ */
1060
+ sqlite3.session_changeset_inverted = (function() {
1061
+ const fnameChangeset = 'sqlite3session_changeset';
1062
+ const fChangeset = Module.cwrap(fnameChangeset, ...decl('nnn:n'));
1063
+ const fnameInvert = 'sqlite3changeset_invert';
1064
+ const fInvert = Module.cwrap(fnameInvert, ...decl('nn:nn'));
1065
+ return function(pSession) {
1066
+ if (typeof pSession !== 'number') {
1067
+ throw new SQLiteError('Invalid session object', SQLite.SQLITE_MISUSE);
1068
+ }
1069
+
1070
+ // Allocate memory for the size (int) and the changeset pointer (void*)
1071
+ const sizePtr = Module._malloc(4);
1072
+ const changesetPtrPtr = Module._malloc(4);
1073
+
1074
+ // Allocate memory for the size (int) and the inverted changeset pointer (void*)
1075
+ const sizePtrInvert = Module._malloc(4);
1076
+ const changesetPtrPtrInvert = Module._malloc(4);
1077
+
1078
+ try {
1079
+ const changesetResult = fChangeset(pSession, sizePtr, changesetPtrPtr);
1080
+ if (changesetResult !== SQLite.SQLITE_OK) {
1081
+ return check(fnameChangeset, changesetResult);
1082
+ }
1083
+
1084
+ // Get the size of the changeset
1085
+ const size = Module.getValue(sizePtr, 'i32');
1086
+ // Get the pointer to the changeset
1087
+ const changesetPtr = Module.getValue(changesetPtrPtr, 'i32');
1088
+
1089
+
1090
+ const invertedResult = fInvert(size, changesetPtr, sizePtrInvert, changesetPtrPtrInvert);
1091
+
1092
+ if (invertedResult !== SQLite.SQLITE_OK) {
1093
+ return check(fnameInvert, invertedResult);
1094
+ }
1095
+
1096
+ // Get the size of the changeset
1097
+ const sizeInvert = Module.getValue(sizePtrInvert, 'i32');
1098
+ // Get the pointer to the changeset
1099
+ const changesetPtrInvert = Module.getValue(changesetPtrPtrInvert, 'i32');
1100
+
1101
+ // Copy the changeset data
1102
+ const changesetInvert = new Uint8Array(Module.HEAPU8.buffer, changesetPtrInvert, sizeInvert);
1103
+
1104
+ Module._sqlite3_free(changesetPtr);
1105
+ Module._sqlite3_free(changesetPtrInvert)
1106
+
1107
+ // Return a copy of the changeset
1108
+ return {
1109
+ result: changesetResult,
1110
+ size: size,
1111
+ changeset: new Uint8Array(changesetInvert)
1112
+ };
1113
+ } finally {
1114
+ // Free the allocated memory
1115
+ Module._free(sizePtr);
1116
+ Module._free(changesetPtrPtr);
1117
+ Module._free(sizePtrInvert);
1118
+ Module._free(changesetPtrPtrInvert);
1119
+ }
1120
+
1121
+ };
1122
+ })();
1123
+
1124
+ sqlite3.changeset_apply = (function() {
1125
+ const fname = 'sqlite3changeset_apply';
1126
+ const f = Module.cwrap(fname, ...decl('nnnnnn:n'));
1127
+ return function(db, changesetData, options) {
1128
+ /*
1129
+ int sqlite3changeset_apply(
1130
+ sqlite3 *db, Apply change to "main" db of this handle
1131
+ int nChangeset, Size of changeset in bytes
1132
+ void *pChangeset, Changeset blob
1133
+ int(*xFilter)(
1134
+ void *pCtx, Copy of sixth arg to _apply()
1135
+ const char *zTab Table name
1136
+ ),
1137
+ int(*xConflict)(
1138
+ void *pCtx, Copy of sixth arg to _apply()
1139
+ int eConflict, DATA, MISSING, CONFLICT, CONSTRAINT
1140
+ sqlite3_changeset_iter *p Handle describing change and conflict
1141
+ ),
1142
+ void *pCtx First argument passed to xConflict
1143
+ );
1144
+ */
1145
+ const inPtr = Module._sqlite3_malloc(changesetData.length);
1146
+ Module.HEAPU8.subarray(inPtr).set(changesetData);
1147
+
1148
+ // https://sqlite.org/session/c_changeset_abort.html
1149
+ const SQLITE_CHANGESET_REPLACE = 1
1150
+ const onConflict = () => {
1151
+ return SQLITE_CHANGESET_REPLACE;
1152
+ }
1153
+
1154
+ const result = f(db, changesetData.length, inPtr, null, onConflict, null);
1155
+
1156
+ if (result !== SQLite.SQLITE_OK) {
1157
+ check(fname, result);
1158
+ }
1159
+
1160
+ return result;
1161
+ }
1162
+ })();
1163
+
1164
+ // Session extension bindings end
1165
+
797
1166
  sqlite3.value = function(pValue) {
798
1167
  const type = sqlite3.value_type(pValue);
799
1168
  switch (type) {
@@ -881,11 +1250,18 @@ export function Factory(Module) {
881
1250
  };
882
1251
  })();
883
1252
 
1253
+ const registeredVfs = new Set();
1254
+
884
1255
  sqlite3.vfs_register = function(vfs, makeDefault) {
1256
+ if (registeredVfs.has(vfs.name)) return
885
1257
  const result = Module.vfs_register(vfs, makeDefault);
886
- return check('sqlite3_vfs_register', result);
1258
+ const res = check('sqlite3_vfs_register', result);
1259
+ registeredVfs.add(vfs.name);
1260
+ return res
887
1261
  };
888
1262
 
1263
+ sqlite3.vfs_registered = registeredVfs;
1264
+
889
1265
  function check(fname, result, db = null, allowed = [SQLite.SQLITE_OK]) {
890
1266
  if (allowed.includes(result)) return result;
891
1267
  const message = db ?
@@ -897,22 +1273,22 @@ export function Factory(Module) {
897
1273
  // This function is used to automatically retry failed calls that
898
1274
  // have pending retry operations that should allow the retry to
899
1275
  // succeed.
900
- // async function retry(f) {
901
- // let rc;
902
- // do {
903
- // // Wait for all pending retry operations to complete. This is
904
- // // normally empty on the first loop iteration.
905
- // if (Module.retryOps.length) {
906
- // await Promise.all(Module.retryOps);
907
- // Module.retryOps = [];
908
- // }
1276
+ async function retry(f) {
1277
+ let rc;
1278
+ do {
1279
+ // Wait for all pending retry operations to complete. This is
1280
+ // normally empty on the first loop iteration.
1281
+ if (Module.retryOps.length) {
1282
+ await Promise.all(Module.retryOps);
1283
+ Module.retryOps = [];
1284
+ }
909
1285
 
910
- // rc = await f();
1286
+ rc = await f();
911
1287
 
912
- // // Retry on failure with new pending retry operations.
913
- // } while (rc && Module.retryOps.length);
914
- // return rc;
915
- // }
1288
+ // Retry on failure with new pending retry operations.
1289
+ } while (rc && Module.retryOps.length);
1290
+ return rc;
1291
+ }
916
1292
 
917
1293
  return sqlite3;
918
1294
  }
@@ -1,6 +1,6 @@
1
- // declare namespace Asyncify {
2
- // function handleAsync(f: () => Promise<any>);
3
- // }
1
+ declare namespace Asyncify {
2
+ function handleAsync(f: () => Promise<any>);
3
+ }
4
4
 
5
5
  declare function UTF8ToString(ptr: number): string;
6
6
  declare function lengthBytesUTF8(s: string): number;