@crvouga/sqlite-mem 1.3.1 → 1.5.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.
@@ -48,7 +48,7 @@
48
48
  "scope": "select",
49
49
  "predicate": "INDEXED BY / NOT INDEXED is accepted and ignored",
50
50
  "specifiedBehavior": "Query results match the unhinted plan; missing-index errors are not raised.",
51
- "pinnedBy": ["indexes/indexed-by.test.ts", "COMPATIBILITY.md"]
51
+ "pinnedBy": ["tests/contract/indexes/indexed-by.test.ts"]
52
52
  },
53
53
  {
54
54
  "id": "materialized-hint-ignored",
@@ -119,6 +119,27 @@
119
119
  "predicate": "user_version is not restored from SQLM",
120
120
  "specifiedBehavior": "PRAGMA user_version after restore is the default unless re-set.",
121
121
  "pinnedBy": ["PRG-beh-05", "SNP-omit-04"]
122
+ },
123
+ {
124
+ "id": "pragma-setter-noop",
125
+ "scope": "pragma",
126
+ "predicate": "Some pragma setters are accepted but do not change engine state yet",
127
+ "specifiedBehavior": "defer_foreign_keys, recursive_triggers, and application_id getters remain 0 after SET.",
128
+ "pinnedBy": ["PRG-beh-02", "PRG-beh-03", "PRG-beh-07"]
129
+ },
130
+ {
131
+ "id": "generate-series-extension",
132
+ "scope": "tvf",
133
+ "predicate": "generate_series is a sqlite-mem extension",
134
+ "specifiedBehavior": "Present in sqlite-mem; not claimed vs bun:sqlite default inventory.",
135
+ "pinnedBy": ["FTS-series-01"]
136
+ },
137
+ {
138
+ "id": "datetime-localtime-utc",
139
+ "scope": "datetime",
140
+ "predicate": "localtime/utc modifiers do not apply host timezone conversion",
141
+ "specifiedBehavior": "Modifiers are accepted as no-ops; storage remains UTC-like fixed strings.",
142
+ "pinnedBy": ["tests/contract/date-time/modifiers.test.ts"]
122
143
  }
123
144
  ]
124
145
  }
@@ -37,7 +37,10 @@ function divergenceIdFor(id: string): string | undefined {
37
37
  }
38
38
  if (id === "PRG-fn-01" || id === "PRG-comp-01") return "compile-options-function-list";
39
39
  if (id === "PRG-beh-05") return "user-version-snapshot";
40
+ if (id === "PRG-beh-02" || id === "PRG-beh-03" || id === "PRG-beh-07") return "pragma-setter-noop";
41
+ if (id === "FTS-series-01") return "generate-series-extension";
40
42
  if (id === "FTS-chg-01") return "fts-shadow-counters";
43
+ if (id === "DAT-mod-localtime" || id === "datetime-localtime-utc") return "datetime-localtime-utc";
41
44
  if (id === "ATT-att-01") return "attach-empty-schema";
42
45
  if (id === "TOK-07") return "double-quote-string-fallback";
43
46
  if (id === "UNI-surr-01") return "lone-surrogate-bind";
@@ -660,12 +663,12 @@ const TAIL: CatalogSection[] = [
660
663
  ["tl-01", "table_list"],
661
664
  ["tvf-01", "pragma_* TVFs correlated"],
662
665
  ["beh-01", "foreign_keys pragma"],
663
- ["beh-02", "defer_foreign_keys"],
664
- ["beh-03", "recursive_triggers"],
666
+ ["beh-02", "defer_foreign_keys", D, "setter currently no-op; getter returns 0"],
667
+ ["beh-03", "recursive_triggers", D, "setter currently no-op; getter returns 0"],
665
668
  ["beh-04", "case_sensitive_like"],
666
669
  ["beh-05", "user_version get/set", D, "snapshot exclusion"],
667
670
  ["beh-06", "schema_version bumps on DDL"],
668
- ["beh-07", "application_id"],
671
+ ["beh-07", "application_id", D, "setter currently no-op; getter returns 0"],
669
672
  ["health-01", "integrity_check quick_check ok"],
670
673
  ["health-02", "foreign_key_check rows"],
671
674
  ["stor-01", "journal_mode memory default"],
@@ -701,7 +704,7 @@ const TAIL: CatalogSection[] = [
701
704
  ["f34-02", "matchinfo offsets"],
702
705
  ["chg-01", "shadow-table change counters", D, "README"],
703
706
  ["snap-01", "virtual tables not in snapshots", D, "README"],
704
- ["series-01", "generate_series"],
707
+ ["series-01", "generate_series", D, "sqlite-mem extension; not in bun:sqlite default"],
705
708
  ]),
706
709
  section("ATT", "ATTACH / DETACH / schemas", true, [
707
710
  ["att-01", "ATTACH anything is empty in-memory schema", D, "filename ignored for data"],
@@ -815,6 +818,11 @@ const TAIL: CatalogSection[] = [
815
818
  ["win-01", "window frame fuzz", F],
816
819
  ["up-01", "UPSERT fuzz", F],
817
820
  ["date-01", "date modifier fuzz", F],
821
+ ["tlp-01", "ternary logic partitioning metamorphic", F],
822
+ ["norec-01", "NoREC metamorphic rewrite", F],
823
+ ["robust-01", "SqliteError-only / timeout / SQLM bit-flip", F],
824
+ ["slt-01", "sqllogictest vendor corpus differential", F],
825
+ ["dst-01", "mixed dump-after-each DST engine", F],
818
826
  ["prop-01", "snapshot restore idempotence", P],
819
827
  ["prop-02", "insert then delete snapshot bytes", P],
820
828
  ["prop-03", "index-added vs index-free equivalence", P],
@@ -1,15 +1,3 @@
1
1
  {
2
- "ids": [
3
- "CON-fk-09",
4
- "DDL-auto-03",
5
- "FTS-series-01",
6
- "LIM-vals-01",
7
- "PRG-beh-01",
8
- "PRG-beh-02",
9
- "PRG-beh-03",
10
- "PRG-beh-04",
11
- "PRG-beh-05",
12
- "PRG-beh-06",
13
- "PRG-beh-07"
14
- ]
2
+ "ids": []
15
3
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "files": [],
3
+ "prefixes": [],
4
+ "notes": "Add vendor/sqllogictest/test/<file> or <file>#<line> entries when a record is intentionally unsupported."
5
+ }
package/dist/index.js CHANGED
@@ -3804,6 +3804,7 @@ function valueOf(node) {
3804
3804
  return jsonNodeToSql(node, "sql");
3805
3805
  }
3806
3806
  function jsonEachRows(json, path) {
3807
+ if (json === null) return [];
3807
3808
  const root = ensureJson(json);
3808
3809
  const blob = encodeJsonb(root);
3809
3810
  const walk2 = walkJsonbTree(blob);
@@ -3833,6 +3834,7 @@ function jsonEachRows(json, path) {
3833
3834
  return walk2.filter((e) => e.parent === focus.idOffset).map((e) => rowFromWalk(e, true));
3834
3835
  }
3835
3836
  function jsonTreeRows(json, path) {
3837
+ if (json === null) return [];
3836
3838
  const root = ensureJson(json);
3837
3839
  const blob = encodeJsonb(root);
3838
3840
  const walk2 = walkJsonbTree(blob);
@@ -4291,11 +4293,16 @@ function parseDate(value, context) {
4291
4293
  const date = new Date(source);
4292
4294
  return Number.isNaN(date.getTime()) ? null : date;
4293
4295
  }
4294
- function applyModifier(date, modifier) {
4296
+ function applyModifier(date, modifier, flags) {
4295
4297
  const result = new Date(date);
4296
4298
  const normalized = modifier.trim().toLowerCase();
4297
4299
  if (normalized === "unixepoch") return new Date((date.getTime() / 864e5 + JULIAN_UNIX_EPOCH) * 1e3);
4298
4300
  if (normalized === "utc" || normalized === "localtime") return result;
4301
+ if (normalized === "subsec" || normalized === "subsecond") {
4302
+ flags.subsec = true;
4303
+ return result;
4304
+ }
4305
+ if (normalized === "floor" || normalized === "ceiling") return result;
4299
4306
  const weekday = /^weekday\s+([0-6])$/.exec(normalized);
4300
4307
  if (weekday) {
4301
4308
  const target = Number(weekday[1]);
@@ -4334,6 +4341,21 @@ function applyModifier(date, modifier) {
4334
4341
  }
4335
4342
  return result;
4336
4343
  }
4344
+ function applyModifiers(date, modifiers) {
4345
+ const flags = { subsec: false };
4346
+ let current = date;
4347
+ for (const modifier of modifiers) {
4348
+ if (typeof modifier !== "string") return null;
4349
+ const next = applyModifier(current, modifier, flags);
4350
+ if (!next) return null;
4351
+ current = next;
4352
+ }
4353
+ return { date: current, subsec: flags.subsec };
4354
+ }
4355
+ function fromUnixSeconds(seconds) {
4356
+ const date = new Date(seconds * 1e3);
4357
+ return Number.isNaN(date.getTime()) ? null : date;
4358
+ }
4337
4359
  function resolveDate(args, context) {
4338
4360
  let source = args[0];
4339
4361
  let modifiers = args.slice(1);
@@ -4341,17 +4363,26 @@ function resolveDate(args, context) {
4341
4363
  if (modifiers[0] === "unixepoch") {
4342
4364
  const seconds = coerceToNumber(source);
4343
4365
  if (seconds === null) return null;
4344
- source = seconds / 86400 + JULIAN_UNIX_EPOCH;
4345
- modifiers = modifiers.slice(1);
4366
+ const date2 = fromUnixSeconds(seconds);
4367
+ if (!date2) return null;
4368
+ return applyModifiers(date2, modifiers.slice(1));
4369
+ }
4370
+ if (typeof modifiers[0] === "string" && modifiers[0].trim().toLowerCase() === "auto") {
4371
+ const number = coerceToNumber(source);
4372
+ if (number === null) {
4373
+ modifiers = modifiers.slice(1);
4374
+ } else if (number >= 0 && number < 53734845e-1) {
4375
+ source = number;
4376
+ modifiers = modifiers.slice(1);
4377
+ } else {
4378
+ const date2 = fromUnixSeconds(number);
4379
+ if (!date2) return null;
4380
+ return applyModifiers(date2, modifiers.slice(1));
4381
+ }
4346
4382
  }
4347
- let date = parseDate(source, context);
4383
+ const date = parseDate(source, context);
4348
4384
  if (!date) return null;
4349
- for (const modifier of modifiers) {
4350
- if (typeof modifier !== "string") return null;
4351
- date = applyModifier(date, modifier);
4352
- if (!date) return null;
4353
- }
4354
- return date;
4385
+ return applyModifiers(date, modifiers);
4355
4386
  }
4356
4387
  function pad(value, length = 2) {
4357
4388
  return String(value).padStart(length, "0");
@@ -4359,8 +4390,9 @@ function pad(value, length = 2) {
4359
4390
  function isoDate(date) {
4360
4391
  return `${pad(date.getUTCFullYear(), 4)}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`;
4361
4392
  }
4362
- function isoTime(date) {
4363
- return `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}`;
4393
+ function isoTime(date, subsec = false) {
4394
+ const base = `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}`;
4395
+ return subsec ? `${base}.${pad(date.getUTCMilliseconds(), 3)}` : base;
4364
4396
  }
4365
4397
  function dayOfYear(date) {
4366
4398
  return Math.floor(
@@ -4404,24 +4436,24 @@ function formatDate(date, format) {
4404
4436
  var dateTimeFunctions = {
4405
4437
  date(args, context) {
4406
4438
  const value = resolveDate(args, context);
4407
- return value ? isoDate(value) : null;
4439
+ return value ? isoDate(value.date) : null;
4408
4440
  },
4409
4441
  time(args, context) {
4410
4442
  const value = resolveDate(args, context);
4411
- return value ? isoTime(value) : null;
4443
+ return value ? isoTime(value.date, value.subsec) : null;
4412
4444
  },
4413
4445
  datetime(args, context) {
4414
4446
  const value = resolveDate(args, context);
4415
- return value ? `${isoDate(value)} ${isoTime(value)}` : null;
4447
+ return value ? `${isoDate(value.date)} ${isoTime(value.date, value.subsec)}` : null;
4416
4448
  },
4417
4449
  julianday(args, context) {
4418
4450
  const value = resolveDate(args, context);
4419
- return value ? value.getTime() / 864e5 + JULIAN_UNIX_EPOCH : null;
4451
+ return value ? value.date.getTime() / 864e5 + JULIAN_UNIX_EPOCH : null;
4420
4452
  },
4421
4453
  strftime(args, context) {
4422
4454
  if (typeof args[0] !== "string") return null;
4423
4455
  const value = resolveDate(args.slice(1), context);
4424
- return value ? formatDate(value, args[0]) : null;
4456
+ return value ? formatDate(value.date, args[0]) : null;
4425
4457
  },
4426
4458
  current_date(args, context) {
4427
4459
  return dateTimeFunctions.date(args.length === 0 ? ["now"] : args, context);
@@ -4434,7 +4466,7 @@ var dateTimeFunctions = {
4434
4466
  },
4435
4467
  unixepoch(args, context) {
4436
4468
  const value = resolveDate(args.length === 0 ? ["now"] : args, context);
4437
- return value ? Math.floor(value.getTime() / 1e3) : null;
4469
+ return value ? Math.floor(value.date.getTime() / 1e3) : null;
4438
4470
  },
4439
4471
  timediff(args) {
4440
4472
  if (args.length !== 2) return null;
@@ -5828,9 +5860,9 @@ function evalExpr(expr, ctx) {
5828
5860
  return result.rows[0]?.[0] ?? null;
5829
5861
  }
5830
5862
  case "aggregate":
5831
- throw new SqliteError("aggregate expression requires aggregate evaluation", "misuse");
5832
- case "window":
5833
5863
  throw new SqliteError("window expression requires window evaluation", "misuse");
5864
+ case "window":
5865
+ throw new SqliteError("misuse of window function", "misuse");
5834
5866
  case "row":
5835
5867
  throw new SqliteError("row value cannot be used as a scalar value", "misuse");
5836
5868
  }
@@ -8077,8 +8109,17 @@ var Table = class _Table {
8077
8109
  }
8078
8110
  return this.maximumRowid === null ? 1 : incrementRowid(this.maximumRowid);
8079
8111
  }
8112
+ const maxSigned = 9223372036854775807n;
8080
8113
  let candidate = canonicalRowid(this.nextRowid);
8081
- while (this.rows.has(candidate)) candidate = incrementRowid(candidate);
8114
+ while (this.rows.has(candidate)) {
8115
+ if (BigInt(candidate) >= maxSigned) {
8116
+ throw new SqliteError("database or disk is full", "other", "SQLITE_FULL");
8117
+ }
8118
+ candidate = incrementRowid(candidate);
8119
+ }
8120
+ if (BigInt(candidate) > maxSigned) {
8121
+ throw new SqliteError("database or disk is full", "other", "SQLITE_FULL");
8122
+ }
8082
8123
  return candidate;
8083
8124
  }
8084
8125
  advanceNextRowid(rowid) {
@@ -8850,7 +8891,11 @@ var Reader = class {
8850
8891
  return utf8Decode(this.raw(this.u32()));
8851
8892
  }
8852
8893
  json() {
8853
- return JSON.parse(this.text(), jsonReviver);
8894
+ try {
8895
+ return JSON.parse(this.text(), jsonReviver);
8896
+ } catch {
8897
+ throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
8898
+ }
8854
8899
  }
8855
8900
  value() {
8856
8901
  const tag = this.u8();
@@ -8928,6 +8973,14 @@ function encodeDatabaseState(state, runtime) {
8928
8973
  return writer.finish();
8929
8974
  }
8930
8975
  function decodeDatabaseState(snapshot) {
8976
+ try {
8977
+ return decodeDatabaseStateInner(snapshot);
8978
+ } catch (error) {
8979
+ if (error instanceof SqliteError) throw error;
8980
+ throw new SqliteError("invalid or truncated sqlite-mem snapshot", "other");
8981
+ }
8982
+ }
8983
+ function decodeDatabaseStateInner(snapshot) {
8931
8984
  const reader = new Reader(snapshot);
8932
8985
  const magic = reader.raw(4);
8933
8986
  if (!magic.every((byte, index) => byte === MAGIC[index]))
@@ -9080,12 +9133,20 @@ var TransactionManager = class {
9080
9133
  }
9081
9134
  rollback(savepoint) {
9082
9135
  this.requireTransaction("cannot rollback - no transaction is active");
9136
+ const preserved = {
9137
+ totalChanges: this.state.totalChanges,
9138
+ changes: this.state.changes,
9139
+ lastInsertRowid: this.state.lastInsertRowid
9140
+ };
9083
9141
  if (savepoint !== void 0) {
9084
9142
  const index = this.findSavepoint(savepoint);
9085
9143
  const snapshot2 = this.savepoints[index];
9086
9144
  if (!snapshot2) throw new SqliteError(`no such savepoint: ${savepoint}`, "transaction", "SQLITE_ERROR");
9087
9145
  this.state.replaceWith(snapshot2.state, { adopt: true });
9088
9146
  this.prng.setState(snapshot2.prngState);
9147
+ this.state.totalChanges = preserved.totalChanges;
9148
+ this.state.changes = preserved.changes;
9149
+ this.state.lastInsertRowid = preserved.lastInsertRowid;
9089
9150
  this.savepoints.splice(index + 1);
9090
9151
  return;
9091
9152
  }
@@ -9096,6 +9157,9 @@ var TransactionManager = class {
9096
9157
  }
9097
9158
  this.state.replaceWith(snapshot, { adopt: true });
9098
9159
  this.prng.setState(prngState);
9160
+ this.state.totalChanges = preserved.totalChanges;
9161
+ this.state.changes = preserved.changes;
9162
+ this.state.lastInsertRowid = preserved.lastInsertRowid;
9099
9163
  this.transactionSnapshot = null;
9100
9164
  this.transactionPrngState = null;
9101
9165
  this.savepoints = [];
@@ -12758,7 +12822,6 @@ function executeUpdateCore(stmt, env) {
12758
12822
  };
12759
12823
  if (stmt.where && isTruthySql(evalExpr(stmt.where, env.createEvalContext(joined))) !== true) continue;
12760
12824
  matchedScope = joined;
12761
- break;
12762
12825
  }
12763
12826
  if (matchedScope) candidates.push({ row, scope: matchedScope });
12764
12827
  }
@@ -13241,7 +13304,9 @@ function applyReferentialDelete(parent, row, env) {
13241
13304
  for (const candidate of matches) {
13242
13305
  if (constraint.onDelete === "CASCADE") {
13243
13306
  changes += applyReferentialDelete(child, candidate, env);
13307
+ if (fireDeleteTriggers("BEFORE", child, candidate, env) === "ignore") continue;
13244
13308
  removeOne(child, candidate, env);
13309
+ fireDeleteTriggers("AFTER", child, candidate, env);
13245
13310
  changes++;
13246
13311
  } else if (constraint.onDelete === "SET NULL") {
13247
13312
  const updated = updateOne(