@livestore/wa-sqlite 1.0.1-dev.9 → 1.0.3-dev.2

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.2",
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,271 @@ 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
+ // Copy the changeset data
942
+ const changeset = new Uint8Array(Module.HEAPU8.buffer, changesetPtr, size);
943
+
944
+ // Free the allocated changeset memory
945
+ Module._sqlite3_free(changesetPtr);
946
+
947
+ // Return a copy of the changeset
948
+ return {
949
+ result: result,
950
+ size: size,
951
+ changeset: new Uint8Array(changeset)
952
+ };
953
+ }
954
+ return check(fname, result);
955
+ } finally {
956
+ // Free the allocated memory
957
+ Module._free(sizePtr);
958
+ Module._free(changesetPtrPtr);
959
+ }
960
+ };
961
+ })();
962
+
963
+ sqlite3.session_delete = (function() {
964
+ const fname = 'sqlite3session_delete';
965
+ const f = Module.cwrap(fname, ...decl('n:v'));
966
+ return function(pSession) {
967
+ if (typeof pSession !== 'number') {
968
+ throw new SQLiteError('Invalid session object', SQLite.SQLITE_MISUSE);
969
+ }
970
+ const result = f(pSession);
971
+ return result;
972
+ };
973
+ })();
974
+
975
+ sqlite3.changeset_invert = (function() {
976
+ const fname = 'sqlite3changeset_invert';
977
+ const f = Module.cwrap(fname, ...decl('nn:nn'));
978
+ return function(changesetData) {
979
+
980
+ const inPtr = Module._sqlite3_malloc(changesetData.length);
981
+ Module.HEAPU8.subarray(inPtr).set(changesetData);
982
+
983
+ const outLengthPtr = Module._malloc(4);
984
+ const outPtrPtr = Module._malloc(4);
985
+ console.log('changesetData.length', changesetData.length)
986
+ const result = f(changesetData.length, inPtr, outLengthPtr, outPtrPtr);
987
+
988
+ if (result !== SQLite.SQLITE_OK) {
989
+ check(fname, result);
990
+ }
991
+
992
+ // Get the size of the changeset
993
+ const outLength = Module.getValue(outLengthPtr, 'i32');
994
+ // Get the pointer to the changeset
995
+ const changesetOutPtr = Module.getValue(outPtrPtr, 'i32');
996
+
997
+ // Copy the changeset data
998
+ const changesetOut = new Uint8Array(Module.HEAPU8.buffer, changesetOutPtr, outLength);
999
+
1000
+ Module._sqlite3_free(inPtr);
1001
+ Module._sqlite3_free(outLengthPtr);
1002
+ Module._sqlite3_free(outPtrPtr);
1003
+
1004
+ return changesetOut;
1005
+ };
1006
+ })();
1007
+
1008
+ /**
1009
+ * Convenience function to get an inverted changeset from a session
1010
+ * without having to call sqlite3session_changeset() and then sqlite3changeset_invert().
1011
+ * It's more efficient as it's reusing the same memory allocation for the changeset.
1012
+ */
1013
+ sqlite3.session_changeset_inverted = (function() {
1014
+ const fnameChangeset = 'sqlite3session_changeset';
1015
+ const fChangeset = Module.cwrap(fnameChangeset, ...decl('nnn:n'));
1016
+ const fnameInvert = 'sqlite3changeset_invert';
1017
+ const fInvert = Module.cwrap(fnameInvert, ...decl('nn:nn'));
1018
+ return function(pSession) {
1019
+ if (typeof pSession !== 'number') {
1020
+ throw new SQLiteError('Invalid session object', SQLite.SQLITE_MISUSE);
1021
+ }
1022
+
1023
+ // Allocate memory for the size (int) and the changeset pointer (void*)
1024
+ const sizePtr = Module._malloc(4);
1025
+ const changesetPtrPtr = Module._malloc(4);
1026
+
1027
+ // Allocate memory for the size (int) and the inverted changeset pointer (void*)
1028
+ const sizePtrInvert = Module._malloc(4);
1029
+ const changesetPtrPtrInvert = Module._malloc(4);
1030
+
1031
+ try {
1032
+ const changesetResult = fChangeset(pSession, sizePtr, changesetPtrPtr);
1033
+ if (changesetResult !== SQLite.SQLITE_OK) {
1034
+ return check(fnameChangeset, changesetResult);
1035
+ }
1036
+
1037
+ // Get the size of the changeset
1038
+ const size = Module.getValue(sizePtr, 'i32');
1039
+ // Get the pointer to the changeset
1040
+ const changesetPtr = Module.getValue(changesetPtrPtr, 'i32');
1041
+
1042
+
1043
+ const invertedResult = fInvert(size, changesetPtr, sizePtrInvert, changesetPtrPtrInvert);
1044
+
1045
+ if (invertedResult !== SQLite.SQLITE_OK) {
1046
+ return check(fnameInvert, invertedResult);
1047
+ }
1048
+
1049
+ // Get the size of the changeset
1050
+ const sizeInvert = Module.getValue(sizePtrInvert, 'i32');
1051
+ // Get the pointer to the changeset
1052
+ const changesetPtrInvert = Module.getValue(changesetPtrPtrInvert, 'i32');
1053
+
1054
+ // Copy the changeset data
1055
+ const changesetInvert = new Uint8Array(Module.HEAPU8.buffer, changesetPtrInvert, sizeInvert);
1056
+
1057
+ Module._sqlite3_free(changesetPtr);
1058
+ Module._sqlite3_free(changesetPtrInvert)
1059
+
1060
+ // Return a copy of the changeset
1061
+ return {
1062
+ result: changesetResult,
1063
+ size: size,
1064
+ changeset: new Uint8Array(changesetInvert)
1065
+ };
1066
+ } finally {
1067
+ // Free the allocated memory
1068
+ Module._free(sizePtr);
1069
+ Module._free(changesetPtrPtr);
1070
+ Module._free(sizePtrInvert);
1071
+ Module._free(changesetPtrPtrInvert);
1072
+ }
1073
+
1074
+ };
1075
+ })();
1076
+
1077
+ sqlite3.changeset_apply = (function() {
1078
+ const fname = 'sqlite3changeset_apply';
1079
+ const f = Module.cwrap(fname, ...decl('nnnnnn:n'));
1080
+ return function(db, changesetData, options) {
1081
+ /*
1082
+ int sqlite3changeset_apply(
1083
+ sqlite3 *db, Apply change to "main" db of this handle
1084
+ int nChangeset, Size of changeset in bytes
1085
+ void *pChangeset, Changeset blob
1086
+ int(*xFilter)(
1087
+ void *pCtx, Copy of sixth arg to _apply()
1088
+ const char *zTab Table name
1089
+ ),
1090
+ int(*xConflict)(
1091
+ void *pCtx, Copy of sixth arg to _apply()
1092
+ int eConflict, DATA, MISSING, CONFLICT, CONSTRAINT
1093
+ sqlite3_changeset_iter *p Handle describing change and conflict
1094
+ ),
1095
+ void *pCtx First argument passed to xConflict
1096
+ );
1097
+ */
1098
+ const inPtr = Module._sqlite3_malloc(changesetData.length);
1099
+ Module.HEAPU8.subarray(inPtr).set(changesetData);
1100
+
1101
+ // https://sqlite.org/session/c_changeset_abort.html
1102
+ const SQLITE_CHANGESET_REPLACE = 1
1103
+ const onConflict = () => {
1104
+ return SQLITE_CHANGESET_REPLACE;
1105
+ }
1106
+
1107
+ const result = f(db, changesetData.length, inPtr, null, onConflict, null);
1108
+
1109
+ if (result !== SQLite.SQLITE_OK) {
1110
+ check(fname, result);
1111
+ }
1112
+
1113
+ return result;
1114
+ }
1115
+ })();
1116
+
1117
+ // Session extension bindings end
1118
+
797
1119
  sqlite3.value = function(pValue) {
798
1120
  const type = sqlite3.value_type(pValue);
799
1121
  switch (type) {
@@ -881,11 +1203,18 @@ export function Factory(Module) {
881
1203
  };
882
1204
  })();
883
1205
 
1206
+ const registeredVfs = new Set();
1207
+
884
1208
  sqlite3.vfs_register = function(vfs, makeDefault) {
1209
+ if (registeredVfs.has(vfs.name)) return
885
1210
  const result = Module.vfs_register(vfs, makeDefault);
886
- return check('sqlite3_vfs_register', result);
1211
+ const res = check('sqlite3_vfs_register', result);
1212
+ registeredVfs.add(vfs.name);
1213
+ return res
887
1214
  };
888
1215
 
1216
+ sqlite3.vfs_registered = registeredVfs;
1217
+
889
1218
  function check(fname, result, db = null, allowed = [SQLite.SQLITE_OK]) {
890
1219
  if (allowed.includes(result)) return result;
891
1220
  const message = db ?
@@ -897,22 +1226,22 @@ export function Factory(Module) {
897
1226
  // This function is used to automatically retry failed calls that
898
1227
  // have pending retry operations that should allow the retry to
899
1228
  // 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
- // }
1229
+ async function retry(f) {
1230
+ let rc;
1231
+ do {
1232
+ // Wait for all pending retry operations to complete. This is
1233
+ // normally empty on the first loop iteration.
1234
+ if (Module.retryOps.length) {
1235
+ await Promise.all(Module.retryOps);
1236
+ Module.retryOps = [];
1237
+ }
909
1238
 
910
- // rc = await f();
1239
+ rc = await f();
911
1240
 
912
- // // Retry on failure with new pending retry operations.
913
- // } while (rc && Module.retryOps.length);
914
- // return rc;
915
- // }
1241
+ // Retry on failure with new pending retry operations.
1242
+ } while (rc && Module.retryOps.length);
1243
+ return rc;
1244
+ }
916
1245
 
917
1246
  return sqlite3;
918
1247
  }
@@ -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;