@crvouga/sqlite-mem 1.10.0 → 1.12.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/unstable.js CHANGED
@@ -5964,6 +5964,10 @@ var IndexStore = class _IndexStore {
5964
5964
  rememberKeyValues(key, values) {
5965
5965
  this.keyValues.set(key, [...values]);
5966
5966
  }
5967
+ /** Key component values for snapshot encode (binary keys). */
5968
+ keyValuesFor(key) {
5969
+ return [...this.keyValues.get(key) ?? []];
5970
+ }
5967
5971
  get size() {
5968
5972
  return this.entries.size;
5969
5973
  }
@@ -6033,60 +6037,141 @@ function sameRowid(left, right) {
6033
6037
  return typeof left === "bigint" || typeof right === "bigint" ? BigInt(left) === BigInt(right) : left === right;
6034
6038
  }
6035
6039
 
6036
- // src/storage/row.ts
6037
- function normalizeColumnName(name) {
6038
- return name.toLowerCase();
6039
- }
6040
- function rowValues(values) {
6041
- const result = /* @__PURE__ */ new Map();
6042
- const entries = values instanceof Map ? values.entries() : Object.entries(values);
6043
- for (const [name, value] of entries) {
6044
- result.set(normalizeColumnName(name), value);
6040
+ // src/storage/columnar-slab.ts
6041
+ var PACK_NULL = 0;
6042
+ var PACK_I32 = 1;
6043
+ var PACK_I64 = 2;
6044
+ var PACK_F64 = 3;
6045
+ var PACK_TEXT_INTERN = 4;
6046
+ var PACK_TEXT_INLINE = 5;
6047
+ var PACK_BLOB = 6;
6048
+ var PACK_TAGGED = 7;
6049
+ function prepareSlabColumn(column, rowCount, nonNull) {
6050
+ if (column.payload.length > 0) {
6051
+ column.view = new DataView(column.payload.buffer, column.payload.byteOffset, column.payload.byteLength);
6052
+ }
6053
+ if (!column.nullBitmap || nonNull === rowCount) {
6054
+ column.nullBitmap = null;
6055
+ column.nonNullRank = void 0;
6056
+ return column;
6045
6057
  }
6046
- return result;
6047
- }
6048
- function cloneRow(row) {
6049
- const values = new Array(row.values.length);
6050
- for (let i = 0; i < row.values.length; i++) values[i] = cloneSqlValue(row.values[i]);
6051
- return { rowid: row.rowid, values };
6052
- }
6053
- function isValueArray(values) {
6054
- return Array.isArray(values);
6058
+ column.nonNullRank = buildNonNullRank(column.nullBitmap, rowCount);
6059
+ return column;
6055
6060
  }
6056
-
6057
- // src/indexes/keys.ts
6058
- function indexKeyValues(columns, row, ctx, table) {
6059
- return columns.map((column) => {
6060
- const raw = column.expr && ctx ? evalExpr(column.expr, ctx) : table ? table.cell(row, normalizeColumnName(column.name)) : null;
6061
- return normalizeForCollation(raw, column.collate ?? "BINARY");
6062
- });
6063
- }
6064
- function rebuildIndexFromTable(index, table, ctxForRow) {
6065
- index.store.clear();
6066
- for (const row of table.scan()) {
6067
- const ctx = ctxForRow(row);
6068
- if (index.where && isTruthySql(evalExpr(index.where, ctx)) !== true) continue;
6069
- index.store.insert(indexKeyValues(index.columns, row, ctx, table), row.rowid, index.unique);
6061
+ function buildNonNullRank(bits, n) {
6062
+ const rank = new Uint32Array(n);
6063
+ let k = 0;
6064
+ for (let i = 0; i < n; i++) {
6065
+ rank[i] = k;
6066
+ if ((bits[i >> 3] & 1 << (i & 7)) === 0) k++;
6070
6067
  }
6068
+ return rank;
6071
6069
  }
6072
- function tableRowEvalContext(table, row) {
6073
- return {
6074
- functions: defaultFunctionRegistry,
6075
- resolveColumn: (qualifier, name) => {
6076
- if (qualifier && qualifier.toLowerCase() !== table.name.toLowerCase()) {
6077
- throw new SqliteError(`no such column: ${qualifier}.${name}`, "no_such_column");
6078
- }
6079
- const key = name.toLowerCase();
6080
- if (key === "rowid" || key === "_rowid_" || key === "oid") return row.rowid;
6081
- if (!table.hasColumn(key) && key !== "rowid") {
6082
- throw new SqliteError(`no such column: ${name}`, "no_such_column");
6083
- }
6084
- return table.cell(row, key);
6085
- },
6086
- getParameter: () => {
6087
- throw new SqliteError("parameters are not allowed in index predicates", "misuse");
6070
+ var ColumnarSlab = class {
6071
+ buffer;
6072
+ rowCount;
6073
+ rowids;
6074
+ columns;
6075
+ intern;
6076
+ rowidIndex = null;
6077
+ constructor(buffer, rowCount, rowids, columns, intern) {
6078
+ this.buffer = buffer;
6079
+ this.rowCount = rowCount;
6080
+ this.rowids = rowids;
6081
+ this.columns = columns;
6082
+ this.intern = intern;
6083
+ }
6084
+ rowIndex(rowid) {
6085
+ this.rowidIndex ??= new Map(this.rowids.map((id, i) => [canonicalRowid(id), i]));
6086
+ const hit = this.rowidIndex.get(canonicalRowid(rowid));
6087
+ if (hit === void 0) return -1;
6088
+ return hit;
6089
+ }
6090
+ cell(rowIndex, col) {
6091
+ if (rowIndex < 0 || rowIndex >= this.rowCount) return null;
6092
+ const column = this.columns[col];
6093
+ if (!column) return null;
6094
+ if (column.nullBitmap && isNull(column.nullBitmap, rowIndex)) return null;
6095
+ return readSlabCell(column, rowIndex, this.intern);
6096
+ }
6097
+ rowAt(rowIndex) {
6098
+ const values = new Array(this.columns.length);
6099
+ for (let c = 0; c < this.columns.length; c++) values[c] = this.cell(rowIndex, c);
6100
+ return { rowid: this.rowids[rowIndex], values };
6101
+ }
6102
+ get(rowid) {
6103
+ const i = this.rowIndex(rowid);
6104
+ if (i < 0) return void 0;
6105
+ return this.rowAt(i);
6106
+ }
6107
+ *scan() {
6108
+ for (let i = 0; i < this.rowCount; i++) yield this.rowAt(i);
6109
+ }
6110
+ materialize() {
6111
+ const map = /* @__PURE__ */ new Map();
6112
+ for (let i = 0; i < this.rowCount; i++) {
6113
+ const row = this.rowAt(i);
6114
+ map.set(row.rowid, row);
6088
6115
  }
6089
- };
6116
+ return map;
6117
+ }
6118
+ };
6119
+ function canonicalRowid(rowid) {
6120
+ return typeof rowid === "bigint" ? rowid : rowid;
6121
+ }
6122
+ function isNull(bits, i) {
6123
+ return (bits[i >> 3] & 1 << (i & 7)) !== 0;
6124
+ }
6125
+ function nonNullRowIndex(column, rowIndex) {
6126
+ if (!column.nullBitmap) return rowIndex;
6127
+ return column.nonNullRank?.[rowIndex] ?? rowIndex;
6128
+ }
6129
+ function readSlabCell(column, rowIndex, intern) {
6130
+ const pack = column.pack;
6131
+ if (pack === PACK_NULL) return null;
6132
+ const nonNullIndex = nonNullRowIndex(column, rowIndex);
6133
+ const view = column.view;
6134
+ if (pack === PACK_I32) {
6135
+ return view.getInt32(nonNullIndex * 4, true);
6136
+ }
6137
+ if (pack === PACK_I64) {
6138
+ const integer = view.getBigInt64(nonNullIndex * 8, true);
6139
+ return integer <= BigInt(Number.MAX_SAFE_INTEGER) && integer >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(integer) : integer;
6140
+ }
6141
+ if (pack === PACK_F64) {
6142
+ const value = view.getFloat64(nonNullIndex * 8, true);
6143
+ return Number.isInteger(value) && Number.isFinite(value) ? asSqlReal(value) : value;
6144
+ }
6145
+ if (pack === PACK_TEXT_INTERN) {
6146
+ const id = view.getUint32(nonNullIndex * 4, true);
6147
+ return intern[id] ?? "";
6148
+ }
6149
+ if (pack === PACK_TEXT_INLINE) {
6150
+ const offsets = column.inlineOffsets;
6151
+ const start = offsets[nonNullIndex];
6152
+ const end = nonNullIndex + 1 < offsets.length ? offsets[nonNullIndex + 1] : column.payload.length;
6153
+ return utf8Decode(column.payload.subarray(start, end));
6154
+ }
6155
+ if (pack === PACK_BLOB) {
6156
+ const blob = column.blobChunks[nonNullIndex];
6157
+ return blob;
6158
+ }
6159
+ if (pack === PACK_TAGGED) {
6160
+ return column.tagged[nonNullIndex] ?? null;
6161
+ }
6162
+ return null;
6163
+ }
6164
+ function packKindOf(value) {
6165
+ if (value instanceof SqlReal) return PACK_F64;
6166
+ if (typeof value === "bigint") return PACK_I64;
6167
+ if (typeof value === "number" && Number.isInteger(value)) {
6168
+ return value >= -2147483648 && value <= 2147483647 ? PACK_I32 : PACK_I64;
6169
+ }
6170
+ if (typeof value === "number") return PACK_F64;
6171
+ if (value instanceof SqlJsonText) return PACK_TAGGED;
6172
+ if (typeof value === "string") return PACK_TEXT_INTERN;
6173
+ if (value instanceof Uint8Array) return PACK_BLOB;
6174
+ return PACK_TAGGED;
6090
6175
  }
6091
6176
 
6092
6177
  // src/types/strict.ts
@@ -7695,6 +7780,27 @@ var FtsVocabVirtualTable = class _FtsVocabVirtualTable {
7695
7780
  }
7696
7781
  };
7697
7782
 
7783
+ // src/storage/row.ts
7784
+ function normalizeColumnName(name) {
7785
+ return name.toLowerCase();
7786
+ }
7787
+ function rowValues(values) {
7788
+ const result = /* @__PURE__ */ new Map();
7789
+ const entries = values instanceof Map ? values.entries() : Object.entries(values);
7790
+ for (const [name, value] of entries) {
7791
+ result.set(normalizeColumnName(name), value);
7792
+ }
7793
+ return result;
7794
+ }
7795
+ function cloneRow(row) {
7796
+ const values = new Array(row.values.length);
7797
+ for (let i = 0; i < row.values.length; i++) values[i] = cloneSqlValue(row.values[i]);
7798
+ return { rowid: row.rowid, values };
7799
+ }
7800
+ function isValueArray(values) {
7801
+ return Array.isArray(values);
7802
+ }
7803
+
7698
7804
  // src/storage/table.ts
7699
7805
  var EQUALITY_HASH_MIN_ROWS = 16;
7700
7806
  var Table = class _Table {
@@ -7709,6 +7815,8 @@ var Table = class _Table {
7709
7815
  strict;
7710
7816
  /** Clustered PK key string → row for WITHOUT ROWID tables. */
7711
7817
  clusteredRows;
7818
+ /** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
7819
+ slab = null;
7712
7820
  scanCache = null;
7713
7821
  /** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
7714
7822
  equalityHashes = null;
@@ -7772,12 +7880,49 @@ var Table = class _Table {
7772
7880
  return this.integerPrimaryKeyAlias();
7773
7881
  }
7774
7882
  get(rowid) {
7883
+ if (this.slab) {
7884
+ const hit = this.slab.get(rowid);
7885
+ return hit ?? void 0;
7886
+ }
7775
7887
  try {
7776
- return this.rows.get(canonicalRowid(rowid));
7888
+ return this.rows.get(canonicalRowid2(rowid));
7777
7889
  } catch {
7778
7890
  return void 0;
7779
7891
  }
7780
7892
  }
7893
+ /** Row count (slab-backed or map-backed). */
7894
+ rowCount() {
7895
+ return this.slab ? this.slab.rowCount : this.rows.size;
7896
+ }
7897
+ /** Rows sorted by rowid for snapshot encode. */
7898
+ sortedRows() {
7899
+ if (this.slab) return [...this.slab.scan()].map((r) => ({ rowid: r.rowid, values: [...r.values] }));
7900
+ return [...this.rows.values()].sort((a, b) => compareRowids(a.rowid, b.rowid));
7901
+ }
7902
+ /** Attach decoded columnar slab (hydrate path). */
7903
+ attachSlab(slab) {
7904
+ this.slab = slab;
7905
+ this.invalidateScan();
7906
+ }
7907
+ /** Materialize slab into `rows` Map for mutation. */
7908
+ materializeSlab() {
7909
+ if (!this.slab) return;
7910
+ for (const row of this.slab.scan()) this.rows.set(row.rowid, { rowid: row.rowid, values: [...row.values] });
7911
+ this.slab = null;
7912
+ this.invalidateScan();
7913
+ }
7914
+ hasRow(rowid) {
7915
+ if (this.slab) return this.slab.get(rowid) !== void 0;
7916
+ try {
7917
+ return this.rows.has(canonicalRowid2(rowid));
7918
+ } catch {
7919
+ return false;
7920
+ }
7921
+ }
7922
+ allRowids() {
7923
+ if (this.slab) return this.slab.rowids;
7924
+ return this.rows.keys();
7925
+ }
7781
7926
  getByKey(value) {
7782
7927
  if (value === null || this.withoutRowid) return void 0;
7783
7928
  try {
@@ -7821,20 +7966,21 @@ var Table = class _Table {
7821
7966
  if (!rowids || rowids.length === 0) return [];
7822
7967
  const rows = [];
7823
7968
  for (const rowid of rowids) {
7824
- const row = this.rows.get(rowid);
7969
+ const row = this.get(rowid);
7825
7970
  if (row) rows.push(row);
7826
7971
  }
7827
7972
  return rows;
7828
7973
  }
7829
7974
  ensureEqualityHash(columnLower) {
7830
- if (this.frozen || this.rows.size < EQUALITY_HASH_MIN_ROWS) return false;
7975
+ if (this.frozen || this.rowCount() < EQUALITY_HASH_MIN_ROWS) return false;
7831
7976
  const column = this.columns.find((item) => item.nameLower === columnLower);
7832
7977
  if (!column) return false;
7833
7978
  this.equalityHashes ??= /* @__PURE__ */ new Map();
7834
7979
  if (this.equalityHashes.has(columnLower)) return true;
7835
7980
  const map = /* @__PURE__ */ new Map();
7836
7981
  const collate = column.collate ?? "BINARY";
7837
- for (const row of this.rows.values()) {
7982
+ const iter = this.slab ? this.slab.scan() : this.rows.values();
7983
+ for (const row of iter) {
7838
7984
  const key = serializeIndexKey([normalizeForCollation(this.cell(row, columnLower), collate)]);
7839
7985
  if (key === null) continue;
7840
7986
  const bucket = map.get(key);
@@ -7880,7 +8026,7 @@ var Table = class _Table {
7880
8026
  if (supplied.rowid !== void 0) {
7881
8027
  throw new SqliteError(`table ${this.name} has no column named rowid`, "other");
7882
8028
  }
7883
- const rowid2 = canonicalRowid(this.allocateRowid());
8029
+ const rowid2 = canonicalRowid2(this.allocateRowid());
7884
8030
  const candidate2 = { rowid: rowid2, values };
7885
8031
  if (!options?.skipValidate) this.validate(candidate2);
7886
8032
  const clusterKey = this.makeClusterKey(values);
@@ -7899,8 +8045,8 @@ var Table = class _Table {
7899
8045
  const value = this.cell({ rowid: 0, values }, normalizeColumnName(alias.name));
7900
8046
  if (rowid === void 0 && value !== null) rowid = asRowid(value, alias.name);
7901
8047
  }
7902
- rowid = canonicalRowid(rowid ?? this.allocateRowid());
7903
- if (this.rows.has(rowid)) {
8048
+ rowid = canonicalRowid2(rowid ?? this.allocateRowid());
8049
+ if (this.hasRow(rowid)) {
7904
8050
  throw new SqliteError(
7905
8051
  `UNIQUE constraint failed: ${this.name}.rowid`,
7906
8052
  "constraint_primary",
@@ -7917,7 +8063,7 @@ var Table = class _Table {
7917
8063
  }
7918
8064
  update(rowid, updates) {
7919
8065
  this.assertMutable();
7920
- const key = canonicalRowid(rowid);
8066
+ const key = canonicalRowid2(rowid);
7921
8067
  const existing = this.rows.get(key);
7922
8068
  if (!existing) return void 0;
7923
8069
  const values = existing.values.slice();
@@ -7952,7 +8098,7 @@ var Table = class _Table {
7952
8098
  const alias = this.integerPrimaryKeyAlias();
7953
8099
  const aliasRow = { rowid: key, values };
7954
8100
  const targetKey = alias ? asRowid(this.cell(aliasRow, normalizeColumnName(alias.name)), alias.name) : key;
7955
- if (targetKey !== key && this.rows.has(targetKey)) {
8101
+ if (targetKey !== key && this.hasRow(targetKey)) {
7956
8102
  throw new SqliteError(
7957
8103
  `UNIQUE constraint failed: ${this.name}.${alias?.name ?? "rowid"}`,
7958
8104
  "constraint_unique",
@@ -7975,7 +8121,7 @@ var Table = class _Table {
7975
8121
  }
7976
8122
  delete(rowid) {
7977
8123
  this.assertMutable();
7978
- const key = canonicalRowid(rowid);
8124
+ const key = canonicalRowid2(rowid);
7979
8125
  const existing = this.rows.get(key);
7980
8126
  if (!existing) return false;
7981
8127
  if (this.withoutRowid) this.clusteredRows.delete(this.makeClusterKey(existing.values));
@@ -7987,6 +8133,10 @@ var Table = class _Table {
7987
8133
  return this.rows.delete(key);
7988
8134
  }
7989
8135
  *scan() {
8136
+ if (this.slab) {
8137
+ yield* this.slab.scan();
8138
+ return;
8139
+ }
7990
8140
  if (!this.scanCache) {
7991
8141
  if (this.withoutRowid) {
7992
8142
  this.scanCache = [...this.clusteredRows.values()].sort((a, b) => this.comparePrimaryKeys(a, b));
@@ -8006,7 +8156,16 @@ var Table = class _Table {
8006
8156
  });
8007
8157
  copy.nextRowid = this.nextRowid;
8008
8158
  copy.maximumRowid = this.maximumRowid;
8009
- for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
8159
+ if (this.slab) {
8160
+ let max = null;
8161
+ for (const row of this.slab.scan()) {
8162
+ copy.rows.set(row.rowid, cloneRow(row));
8163
+ if (max === null || compareRowids(row.rowid, max) > 0) max = row.rowid;
8164
+ }
8165
+ copy.maximumRowid = max ?? void 0;
8166
+ } else {
8167
+ for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
8168
+ }
8010
8169
  for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
8011
8170
  return copy;
8012
8171
  }
@@ -8015,13 +8174,15 @@ var Table = class _Table {
8015
8174
  }
8016
8175
  assertMutable() {
8017
8176
  if (this.frozen) throw new SqliteError("internal: cannot mutate a frozen table", "other");
8177
+ if (this.slab) this.materializeSlab();
8018
8178
  }
8019
8179
  /** Rebuild clustered storage after snapshot decode or bulk load. */
8020
8180
  rebuildClusteredRows() {
8021
8181
  this.maximumRowid = void 0;
8022
8182
  if (!this.withoutRowid) return;
8023
8183
  this.clusteredRows.clear();
8024
- for (const row of this.rows.values()) {
8184
+ const iter = this.slab ? this.slab.scan() : this.rows.values();
8185
+ for (const row of iter) {
8025
8186
  this.clusteredRows.set(this.makeClusterKey(row.values), row);
8026
8187
  }
8027
8188
  this.invalidateScan();
@@ -8145,15 +8306,19 @@ var Table = class _Table {
8145
8306
  if (!this.columns.some((column) => column.autoincrement)) {
8146
8307
  if (this.maximumRowid === void 0) {
8147
8308
  this.maximumRowid = null;
8148
- for (const rowid of this.rows.keys()) {
8309
+ for (const rowid of this.allRowids()) {
8310
+ if (this.maximumRowid === null || compareRowids(rowid, this.maximumRowid) > 0) this.maximumRowid = rowid;
8311
+ }
8312
+ } else if (this.maximumRowid === null && this.rowCount() > 0) {
8313
+ for (const rowid of this.allRowids()) {
8149
8314
  if (this.maximumRowid === null || compareRowids(rowid, this.maximumRowid) > 0) this.maximumRowid = rowid;
8150
8315
  }
8151
8316
  }
8152
8317
  return this.maximumRowid === null ? 1 : incrementRowid(this.maximumRowid);
8153
8318
  }
8154
8319
  const maxSigned = 9223372036854775807n;
8155
- let candidate = canonicalRowid(this.nextRowid);
8156
- while (this.rows.has(candidate)) {
8320
+ let candidate = canonicalRowid2(this.nextRowid);
8321
+ while (this.hasRow(candidate)) {
8157
8322
  if (BigInt(candidate) >= maxSigned) {
8158
8323
  throw new SqliteError("database or disk is full", "other", "SQLITE_FULL");
8159
8324
  }
@@ -8193,7 +8358,7 @@ function namedFromArray(table, values) {
8193
8358
  return named;
8194
8359
  }
8195
8360
  function asRowid(value, column) {
8196
- if (typeof value === "bigint") return canonicalRowid(value);
8361
+ if (typeof value === "bigint") return canonicalRowid2(value);
8197
8362
  if (typeof value === "number" && Number.isSafeInteger(value)) return value;
8198
8363
  throw new SqliteError(
8199
8364
  `datatype mismatch for INTEGER PRIMARY KEY column: ${column}`,
@@ -8201,7 +8366,7 @@ function asRowid(value, column) {
8201
8366
  "SQLITE_MISMATCH"
8202
8367
  );
8203
8368
  }
8204
- function canonicalRowid(value) {
8369
+ function canonicalRowid2(value) {
8205
8370
  if (typeof value === "number") {
8206
8371
  if (!Number.isSafeInteger(value))
8207
8372
  throw new SqliteError("rowid must be a safe integer or bigint", "datatype_mismatch", "SQLITE_MISMATCH");
@@ -8798,24 +8963,53 @@ function keyOf(name) {
8798
8963
  return name.toLowerCase();
8799
8964
  }
8800
8965
 
8801
- // src/serialization/codec.ts
8802
- var MAGIC = utf8Encode("SQLM");
8803
- var VERSION = 4;
8804
- var VERSION_V1 = 1;
8805
- var VERSION_V2 = 2;
8806
- var VERSION_V3 = 3;
8966
+ // src/serialization/wire.ts
8967
+ function writeVarintU32(w, value) {
8968
+ let v = value >>> 0;
8969
+ while (v >= 128) {
8970
+ w.u8(v & 127 | 128);
8971
+ v >>>= 7;
8972
+ }
8973
+ w.u8(v);
8974
+ }
8975
+ function readVarintU32(r) {
8976
+ let result = 0;
8977
+ let shift = 0;
8978
+ for (; ; ) {
8979
+ const byte = r.u8();
8980
+ result |= (byte & 127) << shift;
8981
+ if ((byte & 128) === 0) return result >>> 0;
8982
+ shift += 7;
8983
+ if (shift > 28) r.fail();
8984
+ }
8985
+ }
8807
8986
  var Writer = class {
8808
- buf = new Uint8Array(4096);
8809
- view = new DataView(this.buf.buffer);
8987
+ buf;
8988
+ view;
8810
8989
  len = 0;
8990
+ constructor(capacity = 4096) {
8991
+ this.buf = new Uint8Array(capacity);
8992
+ this.view = new DataView(this.buf.buffer);
8993
+ }
8994
+ get position() {
8995
+ return this.len;
8996
+ }
8997
+ reserve(capacity) {
8998
+ if (capacity <= this.buf.length) return;
8999
+ const next = new Uint8Array(capacity);
9000
+ next.set(this.buf.subarray(0, this.len));
9001
+ this.buf = next;
9002
+ this.view = new DataView(next.buffer);
9003
+ }
8811
9004
  ensure(needed) {
8812
9005
  if (this.len + needed <= this.buf.length) return;
8813
9006
  let cap = this.buf.length;
8814
9007
  while (cap < this.len + needed) cap *= 2;
8815
- const next = new Uint8Array(cap);
8816
- next.set(this.buf.subarray(0, this.len));
8817
- this.buf = next;
8818
- this.view = new DataView(next.buffer);
9008
+ this.reserve(cap);
9009
+ }
9010
+ align4() {
9011
+ const pad2 = 4 - (this.len & 3) & 3;
9012
+ for (let i = 0; i < pad2; i++) this.u8(0);
8819
9013
  }
8820
9014
  u8(value) {
8821
9015
  this.ensure(1);
@@ -8846,52 +9040,28 @@ var Writer = class {
8846
9040
  this.buf.set(value, this.len);
8847
9041
  this.len += value.length;
8848
9042
  }
8849
- text(value) {
8850
- const bytes = utf8Encode(value);
8851
- this.u32(bytes.length);
8852
- this.raw(bytes);
9043
+ rawAt(offset, value) {
9044
+ if (offset + value.length > this.len) this.fail();
9045
+ this.buf.set(value, offset);
8853
9046
  }
8854
- json(value) {
8855
- this.text(JSON.stringify(value, jsonReplacer));
9047
+ /** Length-prefixed UTF-8 via encodeInto when possible. */
9048
+ text(value) {
9049
+ const encoded = utf8Encode(value);
9050
+ writeVarintU32(this, encoded.length);
9051
+ this.raw(encoded);
8856
9052
  }
8857
- value(value) {
8858
- if (value === null) {
8859
- this.u8(0);
8860
- return;
8861
- }
8862
- if (value instanceof SqlReal) {
8863
- this.u8(2);
8864
- this.f64(value.value);
8865
- return;
8866
- }
8867
- if (typeof value === "bigint" || typeof value === "number" && Number.isInteger(value)) {
8868
- this.u8(1);
8869
- this.i64(BigInt(value));
8870
- return;
8871
- }
8872
- if (typeof value === "number") {
8873
- this.u8(2);
8874
- this.f64(value);
8875
- return;
8876
- }
8877
- if (typeof value === "string") {
8878
- this.u8(3);
8879
- this.text(value);
8880
- return;
8881
- }
8882
- if (value instanceof SqlJsonText) {
8883
- this.u8(3);
8884
- this.text(value.value);
8885
- return;
8886
- }
8887
- this.u8(4);
8888
- this.u32(value.length);
8889
- this.raw(value);
9053
+ /** Write UTF-8 at current position without length prefix (intern blob region). */
9054
+ textBytes(value) {
9055
+ const encoded = utf8Encode(value);
9056
+ this.raw(encoded);
8890
9057
  }
8891
9058
  finish() {
8892
9059
  if (this.len === this.buf.length) return this.buf;
8893
9060
  return this.buf.subarray(0, this.len).slice();
8894
9061
  }
9062
+ fail() {
9063
+ throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
9064
+ }
8895
9065
  };
8896
9066
  var Reader = class {
8897
9067
  constructor(bytes) {
@@ -8901,6 +9071,13 @@ var Reader = class {
8901
9071
  bytes;
8902
9072
  offset = 0;
8903
9073
  view;
9074
+ get position() {
9075
+ return this.offset;
9076
+ }
9077
+ skipAlign4() {
9078
+ const pad2 = 4 - (this.offset & 3) & 3;
9079
+ this.offset += pad2;
9080
+ }
8904
9081
  u8() {
8905
9082
  if (this.offset >= this.bytes.length) this.fail();
8906
9083
  return this.bytes[this.offset++];
@@ -8935,33 +9112,9 @@ var Reader = class {
8935
9112
  this.offset += length;
8936
9113
  return value;
8937
9114
  }
8938
- owned(length) {
8939
- return this.raw(length).slice();
8940
- }
8941
9115
  text() {
8942
- return utf8Decode(this.raw(this.u32()));
8943
- }
8944
- json() {
8945
- try {
8946
- return JSON.parse(this.text(), jsonReviver);
8947
- } catch {
8948
- throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
8949
- }
8950
- }
8951
- value() {
8952
- const tag = this.u8();
8953
- if (tag === 0) return null;
8954
- if (tag === 1) {
8955
- const integer = this.i64();
8956
- return integer <= BigInt(Number.MAX_SAFE_INTEGER) && integer >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(integer) : integer;
8957
- }
8958
- if (tag === 2) {
8959
- const value = this.f64();
8960
- return Number.isInteger(value) && Number.isFinite(value) ? asSqlReal(value) : value;
8961
- }
8962
- if (tag === 3) return this.text();
8963
- if (tag === 4) return this.owned(this.u32());
8964
- this.fail();
9116
+ const length = readVarintU32(this);
9117
+ return utf8Decode(this.raw(length));
8965
9118
  }
8966
9119
  remaining() {
8967
9120
  return this.bytes.length - this.offset;
@@ -8973,409 +9126,321 @@ var Reader = class {
8973
9126
  throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
8974
9127
  }
8975
9128
  };
8976
- function encodeDatabaseState(state, runtime, formatVersion = VERSION) {
8977
- if (formatVersion >= 4) return encodeV4(state, runtime);
8978
- return encodeLegacy(state, runtime, formatVersion);
8979
- }
8980
- function encodeLegacy(state, runtime, formatVersion) {
8981
- const writer = new Writer();
8982
- writer.raw(MAGIC);
8983
- writer.u32(formatVersion);
8984
- writer.u8(state.foreignKeysEnabled ? 1 : 0);
8985
- writer.u32(state.schemaVersion);
8986
- writer.u32(state.changes);
8987
- writer.u32(state.totalChanges);
8988
- writer.value(state.lastInsertRowid);
8989
- writer.u32(state.tables.size);
8990
- for (const table of sortedValues(state.tables)) {
8991
- writer.json({
8992
- name: table.name,
8993
- columns: table.columns,
8994
- constraints: table.constraints,
8995
- indexes: [...table.indexes].sort(compareNames),
8996
- originalSql: table.originalSql,
8997
- withoutRowid: table.withoutRowid || void 0,
8998
- strict: table.strict || void 0
8999
- });
9000
- writer.value(table.nextRowid);
9001
- const rows = [...table.rows.values()].sort((a, b) => compareRowids2(a.rowid, b.rowid));
9002
- writer.u32(rows.length);
9003
- for (const row of rows) {
9004
- writer.value(row.rowid);
9005
- writer.u32(table.columns.length);
9006
- for (const column of table.columns) writer.value(table.cell(row, column.nameLower ?? column.name.toLowerCase()));
9129
+ var BJV_NULL = 0;
9130
+ var BJV_FALSE = 1;
9131
+ var BJV_TRUE = 2;
9132
+ var BJV_I32 = 3;
9133
+ var BJV_I64 = 4;
9134
+ var BJV_F64 = 5;
9135
+ var BJV_INTERN = 6;
9136
+ var BJV_STRING = 7;
9137
+ var BJV_BYTES = 8;
9138
+ var BJV_ARRAY = 9;
9139
+ var BJV_OBJECT = 10;
9140
+ function writeBjv(w, value, forceIntern) {
9141
+ if (value === null || value === void 0) {
9142
+ w.u8(BJV_NULL);
9143
+ return;
9144
+ }
9145
+ if (typeof value === "boolean") {
9146
+ w.u8(value ? BJV_TRUE : BJV_FALSE);
9147
+ return;
9148
+ }
9149
+ if (typeof value === "number") {
9150
+ if (Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) {
9151
+ w.u8(BJV_I32);
9152
+ w.u32(value | 0);
9153
+ return;
9007
9154
  }
9155
+ w.u8(BJV_F64);
9156
+ w.f64(value);
9157
+ return;
9008
9158
  }
9009
- const views = sortedValues(state.views);
9010
- writer.u32(views.length);
9011
- for (const view of views) writer.json(view);
9012
- const indexes = sortedValues(state.indexes);
9013
- writer.u32(indexes.length);
9014
- for (const index of indexes) {
9015
- writer.json({
9016
- name: index.name,
9017
- tableName: index.tableName,
9018
- unique: index.unique,
9019
- columns: index.columns,
9020
- where: index.where,
9021
- originalSql: index.originalSql
9022
- });
9159
+ if (typeof value === "bigint") {
9160
+ w.u8(BJV_I64);
9161
+ w.i64(value);
9162
+ return;
9023
9163
  }
9024
- if (formatVersion >= 3) {
9025
- for (const index of indexes) writeIndexStore(writer, index.store);
9164
+ if (typeof value === "string") {
9165
+ w.u8(BJV_INTERN);
9166
+ writeVarintU32(w, forceIntern(value));
9167
+ return;
9026
9168
  }
9027
- if (formatVersion >= VERSION_V2) {
9028
- writer.u64(runtime.prngState);
9029
- writer.i64(BigInt(Math.trunc(runtime.nowMs)));
9169
+ if (value instanceof Uint8Array) {
9170
+ w.u8(BJV_BYTES);
9171
+ writeVarintU32(w, value.length);
9172
+ w.raw(value);
9173
+ return;
9030
9174
  }
9031
- return writer.finish();
9032
- }
9033
- function decodeDatabaseState(snapshot) {
9034
- try {
9035
- return decodeDatabaseStateInner(snapshot);
9036
- } catch (error) {
9037
- if (error instanceof SqliteError) throw error;
9038
- throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
9175
+ if (Array.isArray(value)) {
9176
+ w.u8(BJV_ARRAY);
9177
+ writeVarintU32(w, value.length);
9178
+ for (const item of value) writeBjv(w, item, forceIntern);
9179
+ return;
9039
9180
  }
9040
- }
9041
- function decodeDatabaseStateInner(snapshot) {
9042
- const reader = new Reader(snapshot);
9043
- const magic = reader.raw(4);
9044
- if (!magic.every((byte, index) => byte === MAGIC[index]))
9045
- throw new SqliteError("invalid sqlite-mem snapshot magic", "other");
9046
- const version = reader.u32();
9047
- if (version < VERSION_V1 || version > VERSION) {
9048
- throw new SqliteError(`unsupported sqlite-mem snapshot version: ${version}`, "snapshot_version", "SQLITE_FORMAT");
9181
+ if (typeof value === "object") {
9182
+ const entries = Object.entries(value);
9183
+ w.u8(BJV_OBJECT);
9184
+ writeVarintU32(w, entries.length);
9185
+ for (const [k, v] of entries) {
9186
+ w.u8(BJV_INTERN);
9187
+ writeVarintU32(w, forceIntern(k));
9188
+ writeBjv(w, v, forceIntern);
9189
+ }
9190
+ return;
9049
9191
  }
9050
- if (version > VERSION_V3) return decodeV4(reader);
9051
- const state = new DatabaseState();
9052
- state.foreignKeysEnabled = reader.u8() !== 0;
9053
- state.schemaVersion = reader.u32();
9054
- state.changes = reader.u32();
9055
- state.totalChanges = reader.u32();
9056
- state.lastInsertRowid = asRowid2(reader.value());
9057
- const tableCount = reader.u32();
9058
- for (let tableIndex = 0; tableIndex < tableCount; tableIndex++) {
9059
- const meta = reader.json();
9060
- const table = new Table(meta.name, meta.columns, {
9061
- constraints: meta.constraints,
9062
- indexes: meta.indexes,
9063
- originalSql: meta.originalSql,
9064
- withoutRowid: meta.withoutRowid ?? false,
9065
- strict: meta.strict ?? false
9066
- });
9067
- table.nextRowid = asRowid2(reader.value());
9068
- const rowCount = reader.u32();
9069
- for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
9070
- const rowid = asRowid2(reader.value());
9071
- const valueCount = reader.u32();
9072
- if (valueCount !== table.columns.length) throw new SqliteError("snapshot row column count mismatch", "other");
9073
- const values = [];
9074
- for (let i = 0; i < valueCount; i++) values.push(reader.value());
9075
- table.rows.set(rowid, { rowid, values });
9192
+ w.u8(BJV_NULL);
9193
+ }
9194
+ function readBjv(r, intern) {
9195
+ const tag = r.u8();
9196
+ switch (tag) {
9197
+ case BJV_NULL:
9198
+ return null;
9199
+ case BJV_FALSE:
9200
+ return false;
9201
+ case BJV_TRUE:
9202
+ return true;
9203
+ case BJV_I32:
9204
+ return r.u32() | 0;
9205
+ case BJV_I64:
9206
+ return r.i64();
9207
+ case BJV_F64:
9208
+ return r.f64();
9209
+ case BJV_INTERN:
9210
+ return intern[readVarintU32(r)] ?? "";
9211
+ case BJV_STRING:
9212
+ return r.text();
9213
+ case BJV_BYTES:
9214
+ return r.raw(readVarintU32(r)).slice();
9215
+ case BJV_ARRAY: {
9216
+ const count = readVarintU32(r);
9217
+ const out = new Array(count);
9218
+ for (let i = 0; i < count; i++) out[i] = readBjv(r, intern);
9219
+ return out;
9076
9220
  }
9077
- table.rebuildClusteredRows();
9078
- state.tables.set(table.name.toLowerCase(), table);
9221
+ case BJV_OBJECT: {
9222
+ const count = readVarintU32(r);
9223
+ const out = {};
9224
+ for (let i = 0; i < count; i++) {
9225
+ const key = readBjv(r, intern);
9226
+ out[key] = readBjv(r, intern);
9227
+ }
9228
+ return out;
9229
+ }
9230
+ default:
9231
+ return r.fail();
9079
9232
  }
9080
- const viewCount = reader.u32();
9081
- for (let index = 0; index < viewCount; index++) {
9082
- const view = reader.json();
9083
- state.views.set(view.name.toLowerCase(), view);
9233
+ }
9234
+ var InternPool = class {
9235
+ counts = /* @__PURE__ */ new Map();
9236
+ ids = /* @__PURE__ */ new Map();
9237
+ list = [];
9238
+ constructor() {
9239
+ this.list.push("");
9240
+ this.ids.set("", 0);
9084
9241
  }
9085
- const indexCount = reader.u32();
9086
- const loadedIndexes = [];
9087
- for (let indexPosition = 0; indexPosition < indexCount; indexPosition++) {
9088
- const meta = reader.json();
9089
- const info = { ...meta, store: new IndexStore(meta.name) };
9090
- state.indexes.set(info.name.toLowerCase(), info);
9091
- loadedIndexes.push(info);
9242
+ count(s) {
9243
+ this.counts.set(s, (this.counts.get(s) ?? 0) + 1);
9092
9244
  }
9093
- if (version >= 3) {
9094
- for (const info of loadedIndexes) {
9095
- info.store = readIndexStore(reader, info.name);
9245
+ /** Assign ids only to strings seen ≥2 times (plus empty string). */
9246
+ finalize() {
9247
+ for (const [s, c] of this.counts) {
9248
+ if (c >= 2 && !this.ids.has(s)) {
9249
+ const id = this.list.length;
9250
+ this.ids.set(s, id);
9251
+ this.list.push(s);
9252
+ }
9096
9253
  }
9097
- } else {
9098
- for (const info of loadedIndexes) rebuildIndexStore(state, info);
9099
- }
9100
- let runtime = null;
9101
- if (version >= VERSION_V2) {
9102
- if (reader.remaining() < 16) throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
9103
- runtime = {
9104
- prngState: reader.u64(),
9105
- nowMs: finiteNowMs(Number(reader.i64()))
9106
- };
9107
9254
  }
9108
- if (!reader.done()) throw new SqliteError("snapshot has trailing data", "other");
9109
- return { state, runtime };
9110
- }
9111
- function writeIndexStore(writer, store) {
9112
- const entries = store.snapshotEntries();
9113
- writer.u32(entries.length);
9114
- for (const entry of entries) {
9115
- writer.text(entry.key);
9116
- writer.u32(entry.rowids.length);
9117
- for (const id of entry.rowids) writer.value(id);
9118
- writer.u32(entry.values.length);
9119
- for (const value of entry.values) writer.value(value);
9255
+ /** Always intern (schema / catalog strings). */
9256
+ forceId(s) {
9257
+ const hit = this.ids.get(s);
9258
+ if (hit !== void 0) return hit;
9259
+ const id = this.list.length;
9260
+ this.ids.set(s, id);
9261
+ this.list.push(s);
9262
+ return id;
9120
9263
  }
9121
- }
9122
- function readIndexStore(reader, name) {
9123
- const count = reader.u32();
9124
- const entries = /* @__PURE__ */ new Map();
9125
- const keyValues = /* @__PURE__ */ new Map();
9264
+ id(s) {
9265
+ const hit = this.ids.get(s);
9266
+ if (hit !== void 0) return hit;
9267
+ return -1;
9268
+ }
9269
+ has(s) {
9270
+ return this.ids.has(s);
9271
+ }
9272
+ };
9273
+ function writeInternTable(w, strings) {
9274
+ writeVarintU32(w, strings.length);
9275
+ if (strings.length === 0) return;
9276
+ const offsets = [];
9277
+ let total = 0;
9278
+ for (const s of strings) {
9279
+ offsets.push(total);
9280
+ total += utf8Encode(s).length;
9281
+ }
9282
+ writeVarintU32(w, total);
9283
+ const base = w.position + offsets.length * 4;
9284
+ for (const off of offsets) w.u32(base + off);
9285
+ for (const s of strings) w.textBytes(s);
9286
+ }
9287
+ function readInternTable(r) {
9288
+ const count = readVarintU32(r);
9289
+ if (count === 0) return [];
9290
+ const total = readVarintU32(r);
9291
+ const offsets = [];
9292
+ for (let i = 0; i < count; i++) offsets.push(r.u32());
9293
+ const blobStart = r.position;
9294
+ const blob = r.raw(total);
9295
+ const intern = new Array(count);
9126
9296
  for (let i = 0; i < count; i++) {
9127
- const key = reader.text();
9128
- const rowidCount = reader.u32();
9129
- const rowids = [];
9130
- for (let r = 0; r < rowidCount; r++) rowids.push(asRowid2(reader.value()));
9131
- const valueCount = reader.u32();
9132
- const values = [];
9133
- for (let v = 0; v < valueCount; v++) values.push(reader.value());
9134
- entries.set(key, rowids);
9135
- keyValues.set(key, values);
9297
+ const start = offsets[i] - blobStart;
9298
+ const end = i + 1 < count ? offsets[i + 1] - blobStart : total;
9299
+ intern[i] = utf8Decode(blob.subarray(start, end));
9136
9300
  }
9137
- return new IndexStore(name, entries, keyValues);
9138
- }
9139
- function rebuildIndexStore(state, info) {
9140
- const table = state.getTable(info.tableName);
9141
- rebuildIndexFromTable(info, table, (row) => tableRowEvalContext(table, row));
9142
- }
9143
- function asRowid2(value) {
9144
- if (typeof value === "number" && Number.isSafeInteger(value) || typeof value === "bigint") return value;
9145
- throw new SqliteError("invalid rowid in snapshot", "other");
9146
- }
9147
- function compareNames(a, b) {
9148
- return a < b ? -1 : a > b ? 1 : 0;
9149
- }
9150
- function finiteNowMs(value) {
9151
- if (!Number.isFinite(value)) return 0;
9152
- const max = 864e13;
9153
- if (value > max) return max;
9154
- if (value < -max) return -max;
9155
- return value;
9156
- }
9157
- function compareRowids2(a, b) {
9158
- const left = typeof a === "bigint" ? a : BigInt(a);
9159
- const right = typeof b === "bigint" ? b : BigInt(b);
9160
- if (left < right) return -1;
9161
- if (left > right) return 1;
9162
- return 0;
9163
- }
9164
- function sortedValues(map) {
9165
- return [...map.values()].sort((a, b) => compareNames(a.name, b.name));
9166
- }
9167
- function jsonReplacer(_key, value) {
9168
- if (typeof value === "bigint") return { $sqlm: "bigint", value: value.toString() };
9169
- if (value instanceof Uint8Array) return { $sqlm: "blob", value: Array.from(value) };
9170
- return value;
9171
- }
9172
- function jsonReviver(_key, value) {
9173
- if (!value || typeof value !== "object" || !("$sqlm" in value)) return value;
9174
- const tagged = value;
9175
- if (tagged.$sqlm === "bigint") return BigInt(tagged.value);
9176
- if (tagged.$sqlm === "blob") return Uint8Array.from(tagged.value);
9177
- return value;
9301
+ return intern;
9178
9302
  }
9179
- var PACK_NULL = 0;
9180
- var PACK_INT = 1;
9181
- var PACK_FLOAT = 2;
9182
- var PACK_TEXT = 3;
9183
- var PACK_BLOB = 4;
9184
- var PACK_TAGGED = 5;
9303
+
9304
+ // src/serialization/codec.ts
9305
+ var MAGIC = utf8Encode("SQLM");
9306
+ var VERSION = 5;
9185
9307
  var AFFINITIES = ["TEXT", "NUMERIC", "INTEGER", "REAL", "BLOB"];
9186
- function encodeV4(state, runtime) {
9187
- const intern = /* @__PURE__ */ new Map();
9188
- const internList = [];
9189
- const internId = (s) => {
9190
- const hit = intern.get(s);
9191
- if (hit !== void 0) return hit;
9192
- const id = internList.length;
9193
- intern.set(s, id);
9194
- internList.push(s);
9195
- return id;
9196
- };
9197
- internId("");
9308
+ function encodeDatabaseState(state, runtime) {
9309
+ const pool = new InternPool();
9198
9310
  const tables = sortedValues(state.tables);
9199
9311
  const views = sortedValues(state.views);
9200
9312
  const indexes = sortedValues(state.indexes);
9313
+ const tableRows = [];
9201
9314
  for (const table of tables) {
9202
- internId(table.name);
9203
- internId(table.originalSql ?? "");
9315
+ pool.count(table.name);
9316
+ pool.count(table.originalSql ?? "");
9204
9317
  for (const column of table.columns) {
9205
- internId(column.name);
9206
- internId(column.typeName ?? "");
9207
- internId(column.collate ?? "");
9318
+ pool.count(column.name);
9319
+ pool.count(column.typeName ?? "");
9320
+ pool.count(column.collate ?? "");
9208
9321
  }
9209
- for (const name of table.indexes) internId(name);
9210
- for (const row of table.rows.values()) {
9322
+ for (const name of table.indexes) pool.count(name);
9323
+ const rows = table.sortedRows();
9324
+ tableRows.push(rows);
9325
+ for (const row of rows) {
9211
9326
  for (const value of row.values) {
9212
- if (typeof value === "string") internId(value);
9213
- else if (value instanceof SqlJsonText) internId(value.value);
9327
+ if (typeof value === "string") pool.count(value);
9328
+ else if (value instanceof SqlJsonText) pool.count(value.value);
9214
9329
  }
9215
9330
  }
9216
9331
  }
9217
- const w = new Writer();
9332
+ for (const view of views) pool.count(JSON.stringify(view));
9333
+ for (const index of indexes) pool.count(index.name);
9334
+ pool.finalize();
9335
+ forceSchemaIntern(pool, tables, views, indexes);
9336
+ const internId = (s) => pool.id(s);
9337
+ const forceId = (s) => pool.forceId(s);
9338
+ const w = new Writer(64 * 1024);
9218
9339
  w.raw(MAGIC);
9219
9340
  w.u32(VERSION);
9220
9341
  w.u8(state.foreignKeysEnabled ? 1 : 0);
9221
9342
  w.u32(state.schemaVersion);
9222
9343
  w.u32(state.changes);
9223
9344
  w.u32(state.totalChanges);
9224
- w.value(state.lastInsertRowid);
9225
- w.u32(internList.length);
9226
- for (const s of internList) w.text(s);
9227
- w.u32(tables.length);
9228
- for (const table of tables) {
9229
- w.u32(internId(table.name));
9230
- w.u32(table.columns.length);
9345
+ writeTaggedValue(w, state.lastInsertRowid, internId);
9346
+ writeInternTable(w, pool.list);
9347
+ writeVarintU32(w, tables.length);
9348
+ for (let t = 0; t < tables.length; t++) {
9349
+ const table = tables[t];
9350
+ writeVarintU32(w, forceId(table.name));
9351
+ writeVarintU32(w, table.columns.length);
9231
9352
  for (const column of table.columns) {
9232
- w.u32(internId(column.name));
9233
- w.u32(internId(column.typeName ?? ""));
9353
+ writeVarintU32(w, forceId(column.name));
9354
+ writeVarintU32(w, forceId(column.typeName ?? ""));
9234
9355
  const aff = AFFINITIES.indexOf(column.affinity);
9235
9356
  w.u8(aff < 0 ? 2 : aff);
9236
9357
  w.u8(
9237
9358
  (column.notNull ? 1 : 0) | (column.primaryKey ? 2 : 0) | (column.autoincrement ? 4 : 0) | (column.unique ? 8 : 0)
9238
9359
  );
9239
- w.u32(internId(column.collate ?? ""));
9360
+ writeVarintU32(w, forceId(column.collate ?? ""));
9240
9361
  if (column.defaultExpr) {
9241
9362
  w.u8(1);
9242
- w.json(column.defaultExpr);
9363
+ writeBjv(w, column.defaultExpr, forceId);
9243
9364
  } else w.u8(0);
9244
9365
  if (column.generated) {
9245
9366
  w.u8(1);
9246
- w.json(column.generated);
9367
+ writeBjv(w, column.generated, forceId);
9247
9368
  } else w.u8(0);
9248
9369
  }
9249
- w.json(table.constraints);
9250
- w.u32(internId(table.originalSql ?? ""));
9370
+ writeBjv(w, table.constraints, forceId);
9371
+ writeVarintU32(w, forceId(table.originalSql ?? ""));
9251
9372
  w.u8((table.withoutRowid ? 1 : 0) | (table.strict ? 2 : 0));
9252
9373
  const indexNames = [...table.indexes].sort(compareNames);
9253
- w.u32(indexNames.length);
9254
- for (const name of indexNames) w.u32(internId(name));
9255
- w.value(table.nextRowid);
9256
- const rows = [...table.rows.values()].sort((a, b) => compareRowids2(a.rowid, b.rowid));
9257
- w.u32(rows.length);
9258
- for (const row of rows) w.value(row.rowid);
9259
- for (let c = 0; c < table.columns.length; c++) {
9260
- writePackedColumn(w, rows, c, internId);
9261
- }
9262
- }
9263
- w.u32(views.length);
9264
- for (const view of views) w.json(view);
9265
- w.u32(indexes.length);
9266
- for (const index of indexes) {
9267
- w.json({
9268
- name: index.name,
9269
- tableName: index.tableName,
9270
- unique: index.unique,
9271
- columns: index.columns,
9272
- where: index.where,
9273
- originalSql: index.originalSql
9274
- });
9275
- }
9374
+ writeVarintU32(w, indexNames.length);
9375
+ for (const name of indexNames) writeVarintU32(w, forceId(name));
9376
+ writeTaggedValue(w, table.nextRowid, internId);
9377
+ const rows = tableRows[t];
9378
+ writeVarintU32(w, rows.length);
9379
+ for (const row of rows) writeTaggedValue(w, row.rowid, internId);
9380
+ for (let c = 0; c < table.columns.length; c++) writePackedColumn(w, rows, c, internId);
9381
+ }
9382
+ writeVarintU32(w, views.length);
9383
+ for (const view of views) writeBjv(w, view, forceId);
9384
+ writeVarintU32(w, indexes.length);
9276
9385
  for (const index of indexes) {
9277
- const entries = index.store.snapshotKeys();
9278
- w.u32(entries.length);
9279
- for (const entry of entries) {
9280
- w.text(entry.key);
9281
- w.u32(entry.rowids.length);
9282
- for (const id of entry.rowids) w.value(id);
9283
- }
9386
+ writeBjv(
9387
+ w,
9388
+ {
9389
+ name: index.name,
9390
+ tableName: index.tableName,
9391
+ unique: index.unique,
9392
+ columns: index.columns,
9393
+ where: index.where,
9394
+ originalSql: index.originalSql
9395
+ },
9396
+ forceId
9397
+ );
9284
9398
  }
9399
+ for (const index of indexes) writeIndexStoreBinary(w, index.store, internId);
9285
9400
  w.u64(runtime.prngState);
9286
9401
  w.i64(BigInt(Math.trunc(runtime.nowMs)));
9287
9402
  return w.finish();
9288
9403
  }
9289
- function writePackedColumn(w, rows, col, internId) {
9290
- const n = rows.length;
9291
- if (n === 0) {
9292
- w.u8(PACK_NULL);
9293
- return;
9294
- }
9295
- const bits = new Uint8Array(n + 7 >> 3);
9296
- let nulls = 0;
9297
- let kind = null;
9298
- for (let i = 0; i < n; i++) {
9299
- const value = rows[i].values[col] ?? null;
9300
- if (value === null) {
9301
- bits[i >> 3] = bits[i >> 3] | 1 << (i & 7);
9302
- nulls++;
9303
- continue;
9304
- }
9305
- const cellKind = packKindOf(value);
9306
- if (kind === null) kind = cellKind;
9307
- else if (kind !== cellKind) kind = PACK_TAGGED;
9308
- }
9309
- if (nulls === n) {
9310
- w.u8(PACK_NULL);
9311
- return;
9312
- }
9313
- const pack = kind ?? PACK_TAGGED;
9314
- w.u8(pack);
9315
- w.raw(bits);
9316
- for (let i = 0; i < n; i++) {
9317
- if (bits[i >> 3] & 1 << (i & 7)) continue;
9318
- const value = rows[i].values[col];
9319
- writePackedCell(w, pack, value, internId);
9404
+ function decodeDatabaseState(snapshot) {
9405
+ try {
9406
+ return decodeInner(snapshot);
9407
+ } catch (error) {
9408
+ if (error instanceof SqliteError) throw error;
9409
+ throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
9320
9410
  }
9321
9411
  }
9322
- function packKindOf(value) {
9323
- if (value instanceof SqlReal) return PACK_FLOAT;
9324
- if (typeof value === "bigint" || typeof value === "number" && Number.isInteger(value)) return PACK_INT;
9325
- if (typeof value === "number") return PACK_FLOAT;
9326
- if (value instanceof SqlJsonText) return PACK_TAGGED;
9327
- if (typeof value === "string") return PACK_TEXT;
9328
- if (value instanceof Uint8Array) return PACK_BLOB;
9329
- return PACK_TAGGED;
9330
- }
9331
- function writePackedCell(w, pack, value, internId) {
9332
- if (pack === PACK_INT) {
9333
- w.i64(typeof value === "bigint" ? value : BigInt(value));
9334
- return;
9335
- }
9336
- if (pack === PACK_FLOAT) {
9337
- w.f64(value instanceof SqlReal ? value.value : value);
9338
- return;
9339
- }
9340
- if (pack === PACK_TEXT) {
9341
- const text2 = value instanceof SqlJsonText ? value.value : value;
9342
- w.u32(internId(text2));
9343
- return;
9344
- }
9345
- if (pack === PACK_BLOB) {
9346
- const blob = value;
9347
- w.u32(blob.length);
9348
- w.raw(blob);
9349
- return;
9412
+ function decodeInner(snapshot) {
9413
+ const r = new Reader(snapshot);
9414
+ const magic = r.raw(4);
9415
+ if (!magic.every((byte, index) => byte === MAGIC[index]))
9416
+ throw new SqliteError("invalid sqlite-mem snapshot magic", "other");
9417
+ const version = r.u32();
9418
+ if (version !== VERSION) {
9419
+ throw new SqliteError(`unsupported sqlite-mem snapshot version: ${version}`, "snapshot_version", "SQLITE_FORMAT");
9350
9420
  }
9351
- w.value(value);
9352
- }
9353
- function decodeV4(reader) {
9354
9421
  const state = new DatabaseState();
9355
- state.foreignKeysEnabled = reader.u8() !== 0;
9356
- state.schemaVersion = reader.u32();
9357
- state.changes = reader.u32();
9358
- state.totalChanges = reader.u32();
9359
- state.lastInsertRowid = asRowid2(reader.value());
9360
- const internCount = reader.u32();
9361
- const intern = [];
9362
- for (let i = 0; i < internCount; i++) intern.push(reader.text());
9422
+ state.foreignKeysEnabled = r.u8() !== 0;
9423
+ state.schemaVersion = r.u32();
9424
+ state.changes = r.u32();
9425
+ state.totalChanges = r.u32();
9426
+ state.lastInsertRowid = asRowid2(readTaggedValue(r, []));
9427
+ const intern = readInternTable(r);
9363
9428
  const str = (id) => intern[id] ?? "";
9364
- const tableCount = reader.u32();
9429
+ const tableCount = readVarintU32(r);
9365
9430
  for (let t = 0; t < tableCount; t++) {
9366
- const name = str(reader.u32());
9367
- const colCount = reader.u32();
9431
+ const name = str(readVarintU32(r));
9432
+ const colCount = readVarintU32(r);
9368
9433
  const columns = [];
9369
9434
  for (let c = 0; c < colCount; c++) {
9370
- const colName = str(reader.u32());
9371
- const typeName = str(reader.u32()) || null;
9372
- const affIndex = reader.u8();
9373
- const flags = reader.u8();
9374
- const collate = str(reader.u32()) || null;
9375
- const hasDefault = reader.u8() === 1;
9376
- const defaultExpr = hasDefault ? reader.json() : null;
9377
- const hasGenerated = reader.u8() === 1;
9378
- const generated = hasGenerated ? reader.json() : null;
9435
+ const colName = str(readVarintU32(r));
9436
+ const typeName = str(readVarintU32(r)) || null;
9437
+ const affIndex = r.u8();
9438
+ const flags = r.u8();
9439
+ const collate = str(readVarintU32(r)) || null;
9440
+ const hasDefault = r.u8() === 1;
9441
+ const defaultExpr = hasDefault ? readBjv(r, intern) : null;
9442
+ const hasGenerated = r.u8() === 1;
9443
+ const generated = hasGenerated ? readBjv(r, intern) : null;
9379
9444
  columns.push({
9380
9445
  name: colName,
9381
9446
  nameLower: colName.toLowerCase(),
@@ -9390,12 +9455,12 @@ function decodeV4(reader) {
9390
9455
  generated
9391
9456
  });
9392
9457
  }
9393
- const constraints = reader.json();
9394
- const originalSql = str(reader.u32()) || null;
9395
- const tableFlags = reader.u8();
9396
- const indexCount2 = reader.u32();
9458
+ const constraints = readBjv(r, intern);
9459
+ const originalSql = str(readVarintU32(r)) || null;
9460
+ const tableFlags = r.u8();
9461
+ const indexNameCount = readVarintU32(r);
9397
9462
  const indexNames = [];
9398
- for (let i = 0; i < indexCount2; i++) indexNames.push(str(reader.u32()));
9463
+ for (let i = 0; i < indexNameCount; i++) indexNames.push(str(readVarintU32(r)));
9399
9464
  const table = new Table(name, columns, {
9400
9465
  constraints,
9401
9466
  indexes: indexNames,
@@ -9403,95 +9468,325 @@ function decodeV4(reader) {
9403
9468
  withoutRowid: (tableFlags & 1) !== 0,
9404
9469
  strict: (tableFlags & 2) !== 0
9405
9470
  });
9406
- table.nextRowid = asRowid2(reader.value());
9407
- const rowCount = reader.u32();
9471
+ table.nextRowid = asRowid2(readTaggedValue(r, intern));
9472
+ const rowCount = readVarintU32(r);
9408
9473
  const rowids = [];
9409
- for (let r = 0; r < rowCount; r++) rowids.push(asRowid2(reader.value()));
9410
- const cols = [];
9411
- for (let c = 0; c < colCount; c++) cols.push(readPackedColumn(reader, rowCount, intern));
9412
- for (let r = 0; r < rowCount; r++) {
9413
- const values = [];
9414
- for (let c = 0; c < colCount; c++) values.push(cols[c][r] ?? null);
9415
- const rowid = rowids[r];
9416
- table.rows.set(rowid, { rowid, values });
9417
- }
9418
- table.rebuildClusteredRows();
9474
+ for (let ri = 0; ri < rowCount; ri++) rowids.push(asRowid2(readTaggedValue(r, intern)));
9475
+ const slabColumns = [];
9476
+ for (let c = 0; c < colCount; c++) slabColumns.push(readPackedColumn(r, rowCount, intern));
9477
+ table.attachSlab(new ColumnarSlab(snapshot, rowCount, rowids, slabColumns, intern));
9478
+ if (table.withoutRowid) table.rebuildClusteredRows();
9419
9479
  state.tables.set(table.name.toLowerCase(), table);
9420
9480
  }
9421
- const viewCount = reader.u32();
9481
+ const viewCount = readVarintU32(r);
9422
9482
  for (let i = 0; i < viewCount; i++) {
9423
- const view = reader.json();
9483
+ const view = readBjv(r, intern);
9424
9484
  state.views.set(view.name.toLowerCase(), view);
9425
9485
  }
9426
- const indexCount = reader.u32();
9486
+ const indexCount = readVarintU32(r);
9427
9487
  const loadedIndexes = [];
9428
9488
  for (let i = 0; i < indexCount; i++) {
9429
- const meta = reader.json();
9489
+ const meta = readBjv(r, intern);
9430
9490
  const info = { ...meta, store: new IndexStore(meta.name) };
9431
9491
  state.indexes.set(info.name.toLowerCase(), info);
9432
9492
  loadedIndexes.push(info);
9433
9493
  }
9434
9494
  for (const info of loadedIndexes) {
9435
- info.store = readIndexStoreKeys(reader, info.name);
9436
- const table = state.tables.get(info.tableName.toLowerCase());
9437
- if (!table) continue;
9438
- for (const entry of info.store.snapshotKeys()) {
9439
- const rowid = entry.rowids[0];
9440
- if (rowid === void 0) continue;
9441
- const row = table.rows.get(rowid);
9442
- if (!row) continue;
9443
- info.store.rememberKeyValues(
9444
- entry.key,
9445
- indexKeyValues(info.columns, row, tableRowEvalContext(table, row), table)
9446
- );
9447
- }
9495
+ info.store = readIndexStoreBinary(r, info.name, intern);
9448
9496
  }
9449
- if (reader.remaining() < 16) throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
9450
- const runtime = { prngState: reader.u64(), nowMs: finiteNowMs(Number(reader.i64())) };
9451
- if (!reader.done()) throw new SqliteError("snapshot has trailing data", "other");
9497
+ if (r.remaining() < 16) throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
9498
+ const runtime = { prngState: r.u64(), nowMs: finiteNowMs(Number(r.i64())) };
9499
+ if (!r.done()) throw new SqliteError("snapshot has trailing data", "other");
9452
9500
  return { state, runtime };
9453
9501
  }
9454
- function readPackedColumn(reader, n, intern) {
9455
- const pack = reader.u8();
9456
- const out = new Array(n);
9457
- if (pack === PACK_NULL) {
9458
- for (let i = 0; i < n; i++) out[i] = null;
9459
- return out;
9502
+ function writeTaggedValue(w, value, internId) {
9503
+ if (value === null) {
9504
+ w.u8(0);
9505
+ return;
9460
9506
  }
9461
- const bits = reader.raw(n + 7 >> 3);
9462
- for (let i = 0; i < n; i++) {
9463
- if (bits[i >> 3] & 1 << (i & 7)) {
9464
- out[i] = null;
9465
- continue;
9507
+ if (value instanceof SqlReal) {
9508
+ w.u8(2);
9509
+ w.f64(value.value);
9510
+ return;
9511
+ }
9512
+ if (typeof value === "bigint" || typeof value === "number" && Number.isInteger(value)) {
9513
+ w.u8(1);
9514
+ w.i64(BigInt(value));
9515
+ return;
9516
+ }
9517
+ if (typeof value === "number") {
9518
+ w.u8(2);
9519
+ w.f64(value);
9520
+ return;
9521
+ }
9522
+ if (typeof value === "string") {
9523
+ const id = internId(value);
9524
+ if (id >= 0) {
9525
+ w.u8(3);
9526
+ writeVarintU32(w, id);
9527
+ return;
9466
9528
  }
9467
- out[i] = readPackedCell(reader, pack, intern);
9529
+ w.u8(4);
9530
+ w.text(value);
9531
+ return;
9468
9532
  }
9469
- return out;
9533
+ if (value instanceof SqlJsonText) {
9534
+ w.u8(4);
9535
+ w.text(value.value);
9536
+ return;
9537
+ }
9538
+ w.u8(5);
9539
+ writeVarintU32(w, value.length);
9540
+ w.raw(value);
9470
9541
  }
9471
- function readPackedCell(reader, pack, intern) {
9472
- if (pack === PACK_INT) {
9473
- const integer = reader.i64();
9542
+ function readTaggedValue(r, intern) {
9543
+ const tag = r.u8();
9544
+ if (tag === 0) return null;
9545
+ if (tag === 1) {
9546
+ const integer = r.i64();
9474
9547
  return integer <= BigInt(Number.MAX_SAFE_INTEGER) && integer >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(integer) : integer;
9475
9548
  }
9476
- if (pack === PACK_FLOAT) {
9477
- const value = reader.f64();
9549
+ if (tag === 2) {
9550
+ const value = r.f64();
9478
9551
  return Number.isInteger(value) && Number.isFinite(value) ? asSqlReal(value) : value;
9479
9552
  }
9480
- if (pack === PACK_TEXT) return intern[reader.u32()] ?? "";
9481
- if (pack === PACK_BLOB) return reader.owned(reader.u32());
9482
- return reader.value();
9553
+ if (tag === 3) return intern[readVarintU32(r)] ?? "";
9554
+ if (tag === 4) return r.text();
9555
+ return r.raw(readVarintU32(r));
9483
9556
  }
9484
- function readIndexStoreKeys(reader, name) {
9485
- const count = reader.u32();
9557
+ function writePackedColumn(w, rows, col, internId) {
9558
+ const n = rows.length;
9559
+ if (n === 0) {
9560
+ w.u8(PACK_NULL);
9561
+ return;
9562
+ }
9563
+ const bits = new Uint8Array(n + 7 >> 3);
9564
+ let nulls = 0;
9565
+ let kind = null;
9566
+ let useInlineText = false;
9567
+ for (let i = 0; i < n; i++) {
9568
+ const value = rows[i].values[col] ?? null;
9569
+ if (value === null) {
9570
+ bits[i >> 3] = bits[i >> 3] | 1 << (i & 7);
9571
+ nulls++;
9572
+ continue;
9573
+ }
9574
+ const cellKind = packKindOf(value);
9575
+ if (kind === null) kind = cellKind;
9576
+ else if (kind !== cellKind) kind = PACK_TAGGED;
9577
+ if (cellKind === PACK_TEXT_INTERN && typeof value === "string" && internId(value) < 0) useInlineText = true;
9578
+ if (value instanceof SqlJsonText) kind = PACK_TAGGED;
9579
+ }
9580
+ if (nulls === n) {
9581
+ w.u8(PACK_NULL);
9582
+ return;
9583
+ }
9584
+ let pack = kind ?? PACK_TAGGED;
9585
+ if (pack === PACK_TEXT_INTERN && useInlineText) pack = PACK_TEXT_INLINE;
9586
+ w.u8(pack);
9587
+ w.raw(bits);
9588
+ if (pack === PACK_I32) {
9589
+ w.align4();
9590
+ for (let i = 0; i < n; i++) {
9591
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9592
+ w.u32(rows[i].values[col]);
9593
+ }
9594
+ return;
9595
+ }
9596
+ if (pack === PACK_I64) {
9597
+ w.align4();
9598
+ for (let i = 0; i < n; i++) {
9599
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9600
+ const v = rows[i].values[col];
9601
+ w.i64(typeof v === "bigint" ? v : BigInt(v));
9602
+ }
9603
+ return;
9604
+ }
9605
+ if (pack === PACK_F64) {
9606
+ w.align4();
9607
+ for (let i = 0; i < n; i++) {
9608
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9609
+ const v = rows[i].values[col];
9610
+ w.f64(v instanceof SqlReal ? v.value : v);
9611
+ }
9612
+ return;
9613
+ }
9614
+ if (pack === PACK_TEXT_INTERN) {
9615
+ w.align4();
9616
+ for (let i = 0; i < n; i++) {
9617
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9618
+ const text2 = rows[i].values[col];
9619
+ w.u32(internId(text2));
9620
+ }
9621
+ return;
9622
+ }
9623
+ if (pack === PACK_TEXT_INLINE) {
9624
+ const offsets = [];
9625
+ let blobLen = 0;
9626
+ for (let i = 0; i < n; i++) {
9627
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9628
+ offsets.push(blobLen);
9629
+ const text2 = rows[i].values[col] instanceof SqlJsonText ? rows[i].values[col].value : rows[i].values[col];
9630
+ blobLen += utf8Encode(text2).length;
9631
+ }
9632
+ writeVarintU32(w, offsets.length);
9633
+ for (const off of offsets) writeVarintU32(w, off);
9634
+ writeVarintU32(w, blobLen);
9635
+ for (let i = 0; i < n; i++) {
9636
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9637
+ const text2 = rows[i].values[col] instanceof SqlJsonText ? rows[i].values[col].value : rows[i].values[col];
9638
+ w.textBytes(text2);
9639
+ }
9640
+ return;
9641
+ }
9642
+ if (pack === PACK_BLOB) {
9643
+ for (let i = 0; i < n; i++) {
9644
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9645
+ const blob = rows[i].values[col];
9646
+ writeVarintU32(w, blob.length);
9647
+ w.raw(blob);
9648
+ }
9649
+ return;
9650
+ }
9651
+ for (let i = 0; i < n; i++) {
9652
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9653
+ writeTaggedValue(w, rows[i].values[col], internId);
9654
+ }
9655
+ }
9656
+ function readPackedColumn(r, n, intern) {
9657
+ const pack = r.u8();
9658
+ if (pack === PACK_NULL) return prepareSlabColumn({ pack, nullBitmap: null, payload: new Uint8Array(0) }, n, 0);
9659
+ const bits = r.raw(n + 7 >> 3);
9660
+ const nonNull = countNonNull(bits, n);
9661
+ if (pack === PACK_I32 || pack === PACK_I64 || pack === PACK_F64 || pack === PACK_TEXT_INTERN) {
9662
+ r.skipAlign4();
9663
+ const width = pack === PACK_I32 || pack === PACK_TEXT_INTERN ? 4 : 8;
9664
+ const payload = r.raw(nonNull * width);
9665
+ return prepareSlabColumn({ pack, nullBitmap: bits, payload }, n, nonNull);
9666
+ }
9667
+ if (pack === PACK_TEXT_INLINE) {
9668
+ const count = readVarintU32(r);
9669
+ const offsets = new Uint32Array(count);
9670
+ for (let i = 0; i < count; i++) offsets[i] = readVarintU32(r);
9671
+ const total = readVarintU32(r);
9672
+ const payload = r.raw(total);
9673
+ return prepareSlabColumn({ pack, nullBitmap: bits, payload, inlineOffsets: offsets }, n, nonNull);
9674
+ }
9675
+ if (pack === PACK_BLOB) {
9676
+ const chunks = [];
9677
+ for (let i = 0; i < n; i++) {
9678
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9679
+ const len = readVarintU32(r);
9680
+ chunks.push(r.raw(len));
9681
+ }
9682
+ return prepareSlabColumn({ pack, nullBitmap: bits, payload: new Uint8Array(0), blobChunks: chunks }, n, nonNull);
9683
+ }
9684
+ const tagged = [];
9685
+ for (let i = 0; i < n; i++) {
9686
+ if (bits[i >> 3] & 1 << (i & 7)) continue;
9687
+ tagged.push(readTaggedValue(r, intern));
9688
+ }
9689
+ return prepareSlabColumn({ pack: PACK_TAGGED, nullBitmap: bits, payload: new Uint8Array(0), tagged }, n, nonNull);
9690
+ }
9691
+ function countNonNull(bits, n) {
9692
+ let c = 0;
9693
+ for (let i = 0; i < n; i++) if ((bits[i >> 3] & 1 << (i & 7)) === 0) c++;
9694
+ return c;
9695
+ }
9696
+ function writeIndexStoreBinary(w, store, internId) {
9697
+ const entries = store.snapshotKeys();
9698
+ writeVarintU32(w, entries.length);
9699
+ for (const entry of entries) {
9700
+ const values = store.keyValuesFor(entry.key);
9701
+ writeVarintU32(w, values.length);
9702
+ for (const v of values) writeTaggedValue(w, v, internId);
9703
+ writeVarintU32(w, entry.rowids.length);
9704
+ for (const id of entry.rowids) writeTaggedValue(w, id, internId);
9705
+ }
9706
+ }
9707
+ function readIndexStoreBinary(r, name, intern) {
9708
+ const count = readVarintU32(r);
9486
9709
  const entries = /* @__PURE__ */ new Map();
9710
+ const keyValues = /* @__PURE__ */ new Map();
9487
9711
  for (let i = 0; i < count; i++) {
9488
- const key = reader.text();
9489
- const rowidCount = reader.u32();
9712
+ const valueCount = readVarintU32(r);
9713
+ const values = [];
9714
+ for (let v = 0; v < valueCount; v++) values.push(readTaggedValue(r, intern));
9715
+ const key = serializeIndexEntry(values);
9716
+ const rowidCount = readVarintU32(r);
9490
9717
  const rowids = [];
9491
- for (let r = 0; r < rowidCount; r++) rowids.push(asRowid2(reader.value()));
9718
+ for (let ri = 0; ri < rowidCount; ri++) rowids.push(asRowid2(readTaggedValue(r, intern)));
9492
9719
  entries.set(key, rowids);
9720
+ keyValues.set(key, values);
9721
+ }
9722
+ return new IndexStore(name, entries, keyValues);
9723
+ }
9724
+ function asRowid2(value) {
9725
+ if (typeof value === "number" && Number.isSafeInteger(value) || typeof value === "bigint") return value;
9726
+ throw new SqliteError("invalid rowid in snapshot", "other");
9727
+ }
9728
+ function compareNames(a, b) {
9729
+ return a < b ? -1 : a > b ? 1 : 0;
9730
+ }
9731
+ function finiteNowMs(value) {
9732
+ if (!Number.isFinite(value)) return 0;
9733
+ const max = 864e13;
9734
+ if (value > max) return max;
9735
+ if (value < -max) return -max;
9736
+ return value;
9737
+ }
9738
+ function sortedValues(map) {
9739
+ return [...map.values()].sort((a, b) => compareNames(a.name, b.name));
9740
+ }
9741
+ function forceSchemaIntern(pool, tables, views, indexes) {
9742
+ for (const table of tables) {
9743
+ pool.forceId(table.name);
9744
+ pool.forceId(table.originalSql ?? "");
9745
+ for (const column of table.columns) {
9746
+ pool.forceId(column.name);
9747
+ pool.forceId(column.typeName ?? "");
9748
+ pool.forceId(column.collate ?? "");
9749
+ }
9750
+ for (const name of table.indexes) pool.forceId(name);
9751
+ forceBjvStrings(pool, table.constraints);
9752
+ for (const column of table.columns) {
9753
+ if (column.defaultExpr) forceBjvStrings(pool, column.defaultExpr);
9754
+ if (column.generated) forceBjvStrings(pool, column.generated);
9755
+ }
9756
+ }
9757
+ for (const view of views) forceBjvStrings(pool, view);
9758
+ for (const index of indexes) {
9759
+ pool.forceId(index.name);
9760
+ pool.forceId(index.tableName);
9761
+ pool.forceId(index.originalSql ?? "");
9762
+ forceBjvStrings(pool, {
9763
+ name: index.name,
9764
+ tableName: index.tableName,
9765
+ unique: index.unique,
9766
+ columns: index.columns,
9767
+ where: index.where,
9768
+ originalSql: index.originalSql
9769
+ });
9770
+ }
9771
+ }
9772
+ function forceBjvStrings(pool, value) {
9773
+ if (value === null || value === void 0) return;
9774
+ if (typeof value === "string") {
9775
+ pool.forceId(value);
9776
+ return;
9777
+ }
9778
+ if (typeof value === "bigint" || typeof value === "boolean" || typeof value === "number") return;
9779
+ if (value instanceof Uint8Array) return;
9780
+ if (Array.isArray(value)) {
9781
+ for (const item of value) forceBjvStrings(pool, item);
9782
+ return;
9783
+ }
9784
+ if (typeof value === "object") {
9785
+ for (const [k, v] of Object.entries(value)) {
9786
+ pool.forceId(k);
9787
+ forceBjvStrings(pool, v);
9788
+ }
9493
9789
  }
9494
- return new IndexStore(name, entries);
9495
9790
  }
9496
9791
  export {
9497
9792
  DEFAULT_DATABASE_SEED,