@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.
- package/compat/divergences.json +22 -1
- package/compat/scenarios.ts +12 -4
- package/compat/smoke-baseline.json +1 -13
- package/compat/sqllogictest-skip.json +5 -0
- package/dist/index.js +88 -23
- package/dist/index.js.map +3 -3
- package/dist/unstable.js +73 -22
- package/dist/unstable.js.map +3 -3
- package/package.json +6 -1
package/compat/divergences.json
CHANGED
|
@@ -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"
|
|
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
|
}
|
package/compat/scenarios.ts
CHANGED
|
@@ -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],
|
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
|
-
|
|
4345
|
-
|
|
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
|
-
|
|
4383
|
+
const date = parseDate(source, context);
|
|
4348
4384
|
if (!date) return null;
|
|
4349
|
-
|
|
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
|
-
|
|
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))
|
|
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
|
-
|
|
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(
|