@crvouga/sqlite-mem 1.1.2 → 1.2.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/AGENTS.md +4 -1
- package/COMPATIBILITY-AUDIT.md +29 -15
- package/COMPATIBILITY.md +16 -8
- package/README.md +15 -12
- package/compat/coverage.json +779 -729
- package/compat/divergences.json +124 -0
- package/compat/requirements.json +43 -43
- package/compat/scenario-types.ts +63 -0
- package/compat/scenarios.ts +851 -0
- package/compat/smoke-baseline.json +15 -0
- package/dist/api/database.d.ts +12 -3
- package/dist/api/statement.d.ts +1 -1
- package/dist/ast/nodes.d.ts +3 -0
- package/dist/executor/env.d.ts +1 -1
- package/dist/executor/select.d.ts +2 -1
- package/dist/executor/triggers.d.ts +2 -2
- package/dist/expressions/context.d.ts +3 -1
- package/dist/expressions/like.d.ts +3 -2
- package/dist/functions/registry.d.ts +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +655 -185
- package/dist/index.js.map +4 -4
- package/dist/parser/parser.d.ts +2 -2
- package/dist/runtime/clock.d.ts +4 -2
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/options.d.ts +11 -3
- package/dist/runtime/prng.d.ts +11 -0
- package/dist/storage/database-state.d.ts +2 -0
- package/dist/storage/table.d.ts +2 -0
- package/dist/types/value.d.ts +5 -0
- package/dist/unstable.d.ts +3 -3
- package/dist/unstable.js +243 -64
- package/dist/unstable.js.map +3 -3
- package/dist/vtable/fts/table.d.ts +2 -3
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -745,13 +745,20 @@ var Parser = class {
|
|
|
745
745
|
const stmt = this.parseStatement();
|
|
746
746
|
return { type: "explain", queryPlan, statement: stmt };
|
|
747
747
|
}
|
|
748
|
-
if (this.
|
|
748
|
+
if (this.at("WITH")) {
|
|
749
|
+
const withClause = this.parseWithClause();
|
|
750
|
+
if (this.at("SELECT")) return this.parseSelectStmt(withClause);
|
|
751
|
+
if (this.at("INSERT") || this.at("REPLACE")) return this.parseInsertStmt(withClause);
|
|
752
|
+
if (this.at("UPDATE")) return this.parseUpdateStmt(withClause);
|
|
753
|
+
if (this.at("DELETE")) return this.parseDeleteStmt(withClause);
|
|
754
|
+
this.syntaxError("expected SELECT, INSERT, UPDATE, or DELETE after WITH clause");
|
|
755
|
+
}
|
|
749
756
|
if (this.at("SELECT")) return this.parseSelectStmt();
|
|
750
|
-
if (this.
|
|
757
|
+
if (this.at("INSERT") || this.at("REPLACE")) {
|
|
751
758
|
return this.parseInsertStmt();
|
|
752
759
|
}
|
|
753
|
-
if (this.
|
|
754
|
-
if (this.
|
|
760
|
+
if (this.at("UPDATE")) return this.parseUpdateStmt();
|
|
761
|
+
if (this.at("DELETE")) return this.parseDeleteStmt();
|
|
755
762
|
if (this.at("CREATE")) return this.parseCreateStmt();
|
|
756
763
|
if (this.at("DROP")) return this.parseDropStmt();
|
|
757
764
|
if (this.at("ALTER")) return this.parseAlterTableStmt();
|
|
@@ -813,12 +820,15 @@ var Parser = class {
|
|
|
813
820
|
this.expect("RPAREN");
|
|
814
821
|
}
|
|
815
822
|
this.expect("AS");
|
|
816
|
-
|
|
817
|
-
|
|
823
|
+
let materialized = null;
|
|
824
|
+
if (this.match("NOT")) {
|
|
825
|
+
this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
|
|
826
|
+
materialized = "not_materialized";
|
|
827
|
+
} else if (this.match("MATERIALIZED")) materialized = "materialized";
|
|
818
828
|
this.expect("LPAREN");
|
|
819
|
-
const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.parseSelectCore();
|
|
829
|
+
const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.at("WITH") ? this.parseSelectStmt() : this.parseSelectCore();
|
|
820
830
|
this.expect("RPAREN");
|
|
821
|
-
ctes.push({ name, columns, select });
|
|
831
|
+
ctes.push({ name, columns, materialized, select });
|
|
822
832
|
} while (this.match("COMMA"));
|
|
823
833
|
return { recursive, ctes };
|
|
824
834
|
}
|
|
@@ -826,8 +836,7 @@ var Parser = class {
|
|
|
826
836
|
return this.at("WITH") ? this.parseWithClause() : null;
|
|
827
837
|
}
|
|
828
838
|
// ── SELECT ──────────────────────────────────────────────────────────────
|
|
829
|
-
parseSelectStmt() {
|
|
830
|
-
const withClause = this.parseOptionalWith();
|
|
839
|
+
parseSelectStmt(withClause = this.parseOptionalWith()) {
|
|
831
840
|
const select = this.parseSelectCore();
|
|
832
841
|
select.with = withClause;
|
|
833
842
|
return select;
|
|
@@ -1125,8 +1134,7 @@ var Parser = class {
|
|
|
1125
1134
|
return { limit: first, offset };
|
|
1126
1135
|
}
|
|
1127
1136
|
// ── INSERT / REPLACE ────────────────────────────────────────────────────
|
|
1128
|
-
parseInsertStmt() {
|
|
1129
|
-
const withClause = this.parseOptionalWith();
|
|
1137
|
+
parseInsertStmt(withClause = this.parseOptionalWith()) {
|
|
1130
1138
|
let mode = "insert";
|
|
1131
1139
|
if (this.match("REPLACE")) {
|
|
1132
1140
|
mode = "replace";
|
|
@@ -1229,8 +1237,7 @@ var Parser = class {
|
|
|
1229
1237
|
return this.parseResultColumns();
|
|
1230
1238
|
}
|
|
1231
1239
|
// ── UPDATE ──────────────────────────────────────────────────────────────
|
|
1232
|
-
parseUpdateStmt() {
|
|
1233
|
-
const withClause = this.parseOptionalWith();
|
|
1240
|
+
parseUpdateStmt(withClause = this.parseOptionalWith()) {
|
|
1234
1241
|
this.expect("UPDATE");
|
|
1235
1242
|
const or = this.mapUpdateOr(this.parseOrConflict());
|
|
1236
1243
|
const table = this.parseTableName();
|
|
@@ -1245,8 +1252,7 @@ var Parser = class {
|
|
|
1245
1252
|
return { type: "update", with: withClause, or, table, alias, set, from, where, returning };
|
|
1246
1253
|
}
|
|
1247
1254
|
// ── DELETE ──────────────────────────────────────────────────────────────
|
|
1248
|
-
parseDeleteStmt() {
|
|
1249
|
-
const withClause = this.parseOptionalWith();
|
|
1255
|
+
parseDeleteStmt(withClause = this.parseOptionalWith()) {
|
|
1250
1256
|
this.expect("DELETE");
|
|
1251
1257
|
this.expect("FROM");
|
|
1252
1258
|
const table = this.parseTableName();
|
|
@@ -1915,6 +1921,24 @@ var Parser = class {
|
|
|
1915
1921
|
left = this.parseLikeRhs(left, true, "GLOB");
|
|
1916
1922
|
continue;
|
|
1917
1923
|
}
|
|
1924
|
+
if (n.kind === "REGEXP") {
|
|
1925
|
+
this.advance();
|
|
1926
|
+
this.advance();
|
|
1927
|
+
const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
|
|
1928
|
+
left = {
|
|
1929
|
+
type: "unary",
|
|
1930
|
+
op: "NOT",
|
|
1931
|
+
expr: {
|
|
1932
|
+
type: "function",
|
|
1933
|
+
name: "REGEXP",
|
|
1934
|
+
distinct: false,
|
|
1935
|
+
args: [pattern, left],
|
|
1936
|
+
orderBy: [],
|
|
1937
|
+
filter: null
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1918
1942
|
if (n.kind === "BETWEEN") {
|
|
1919
1943
|
this.advance();
|
|
1920
1944
|
this.advance();
|
|
@@ -1970,6 +1994,12 @@ var Parser = class {
|
|
|
1970
1994
|
left = this.parseLikeRhs(left, false, "GLOB");
|
|
1971
1995
|
continue;
|
|
1972
1996
|
}
|
|
1997
|
+
if (this.at("REGEXP") && PREC.IS_IN_LIKE >= minPrec) {
|
|
1998
|
+
this.advance();
|
|
1999
|
+
const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
|
|
2000
|
+
left = { type: "function", name: "REGEXP", distinct: false, args: [pattern, left], orderBy: [], filter: null };
|
|
2001
|
+
continue;
|
|
2002
|
+
}
|
|
1973
2003
|
if (this.at("MATCH") && PREC.IS_IN_LIKE >= minPrec) {
|
|
1974
2004
|
this.advance();
|
|
1975
2005
|
const right2 = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
|
|
@@ -2128,7 +2158,7 @@ var Parser = class {
|
|
|
2128
2158
|
}
|
|
2129
2159
|
if (this.at("CURRENT_DATE") || this.at("CURRENT_TIME") || this.at("CURRENT_TIMESTAMP")) {
|
|
2130
2160
|
const tok = this.advance();
|
|
2131
|
-
return { type: "function", name: tok.value, distinct: false, args: [], filter: null };
|
|
2161
|
+
return { type: "function", name: tok.value, distinct: false, args: [], orderBy: [], filter: null };
|
|
2132
2162
|
}
|
|
2133
2163
|
if (this.at("NUMBER")) {
|
|
2134
2164
|
const tok = this.advance();
|
|
@@ -2211,6 +2241,7 @@ var Parser = class {
|
|
|
2211
2241
|
} while (this.match("COMMA"));
|
|
2212
2242
|
}
|
|
2213
2243
|
}
|
|
2244
|
+
const orderBy = this.parseOrderBy();
|
|
2214
2245
|
this.expect("RPAREN");
|
|
2215
2246
|
if (this.match("FILTER")) {
|
|
2216
2247
|
this.expect("LPAREN");
|
|
@@ -2231,13 +2262,13 @@ var Parser = class {
|
|
|
2231
2262
|
window = this.parseWindowSpec();
|
|
2232
2263
|
this.expect("RPAREN");
|
|
2233
2264
|
}
|
|
2234
|
-
const func = isAgg ? { type: "aggregate", name: upper, distinct, args, filter } : { type: "function", name, distinct, args, filter };
|
|
2265
|
+
const func = isAgg ? { type: "aggregate", name: upper, distinct, args, orderBy, filter } : { type: "function", name, distinct, args, orderBy, filter };
|
|
2235
2266
|
return { type: "window", func, window };
|
|
2236
2267
|
}
|
|
2237
2268
|
if (isAgg) {
|
|
2238
|
-
return { type: "aggregate", name: upper, distinct, args, filter };
|
|
2269
|
+
return { type: "aggregate", name: upper, distinct, args, orderBy, filter };
|
|
2239
2270
|
}
|
|
2240
|
-
return { type: "function", name, distinct, args, filter };
|
|
2271
|
+
return { type: "function", name, distinct, args, orderBy, filter };
|
|
2241
2272
|
}
|
|
2242
2273
|
parseCaseExpr(base) {
|
|
2243
2274
|
const whens = [];
|
|
@@ -2345,8 +2376,12 @@ function fixedClock(instant = DEFAULT_NOW) {
|
|
|
2345
2376
|
if (Number.isNaN(ms)) throw new RangeError("invalid clock instant");
|
|
2346
2377
|
return () => new Date(ms);
|
|
2347
2378
|
}
|
|
2379
|
+
function systemClock() {
|
|
2380
|
+
return () => /* @__PURE__ */ new Date();
|
|
2381
|
+
}
|
|
2348
2382
|
function resolveClock(now) {
|
|
2349
2383
|
if (now === void 0) return fixedClock(DEFAULT_NOW);
|
|
2384
|
+
if (now === "system") return systemClock();
|
|
2350
2385
|
if (typeof now === "function") return () => new Date(now().getTime());
|
|
2351
2386
|
return fixedClock(now);
|
|
2352
2387
|
}
|
|
@@ -2411,6 +2446,28 @@ var Prng = class _Prng {
|
|
|
2411
2446
|
return copy;
|
|
2412
2447
|
}
|
|
2413
2448
|
};
|
|
2449
|
+
var OsEntropy = class _OsEntropy extends Prng {
|
|
2450
|
+
constructor() {
|
|
2451
|
+
super(1);
|
|
2452
|
+
}
|
|
2453
|
+
nextU64() {
|
|
2454
|
+
const bytes = new Uint8Array(8);
|
|
2455
|
+
crypto.getRandomValues(bytes);
|
|
2456
|
+
let value = 0n;
|
|
2457
|
+
for (let i = 0; i < 8; i++) {
|
|
2458
|
+
value |= BigInt(bytes[i]) << BigInt(i * 8);
|
|
2459
|
+
}
|
|
2460
|
+
return BigInt.asUintN(64, value);
|
|
2461
|
+
}
|
|
2462
|
+
getState() {
|
|
2463
|
+
return 0n;
|
|
2464
|
+
}
|
|
2465
|
+
setState(_state) {
|
|
2466
|
+
}
|
|
2467
|
+
clone() {
|
|
2468
|
+
return new _OsEntropy();
|
|
2469
|
+
}
|
|
2470
|
+
};
|
|
2414
2471
|
|
|
2415
2472
|
// src/types/value.ts
|
|
2416
2473
|
var SqlReal = class {
|
|
@@ -2517,6 +2574,23 @@ function applyAffinity(value, affinity) {
|
|
|
2517
2574
|
return value;
|
|
2518
2575
|
}
|
|
2519
2576
|
}
|
|
2577
|
+
function applyComparisonAffinity(left, right, leftAffinity, rightAffinity) {
|
|
2578
|
+
const leftNumeric = leftAffinity === "INTEGER" || leftAffinity === "REAL" || leftAffinity === "NUMERIC";
|
|
2579
|
+
const rightNumeric = rightAffinity === "INTEGER" || rightAffinity === "REAL" || rightAffinity === "NUMERIC";
|
|
2580
|
+
const leftNone = leftAffinity === null || leftAffinity === "BLOB";
|
|
2581
|
+
const rightNone = rightAffinity === null || rightAffinity === "BLOB";
|
|
2582
|
+
if (leftNumeric && (rightAffinity === "TEXT" || rightNone)) {
|
|
2583
|
+
const affinity = leftAffinity === "REAL" ? "REAL" : "NUMERIC";
|
|
2584
|
+
return [left, applyAffinity(right, affinity)];
|
|
2585
|
+
}
|
|
2586
|
+
if (rightNumeric && (leftAffinity === "TEXT" || leftNone)) {
|
|
2587
|
+
const affinity = rightAffinity === "REAL" ? "REAL" : "NUMERIC";
|
|
2588
|
+
return [applyAffinity(left, affinity), right];
|
|
2589
|
+
}
|
|
2590
|
+
if (leftAffinity === "TEXT" && rightNone) return [left, applyAffinity(right, "TEXT")];
|
|
2591
|
+
if (rightAffinity === "TEXT" && leftNone) return [applyAffinity(left, "TEXT"), right];
|
|
2592
|
+
return [left, right];
|
|
2593
|
+
}
|
|
2520
2594
|
function coerceToNumber(value) {
|
|
2521
2595
|
if (value === null) return null;
|
|
2522
2596
|
if (value instanceof SqlReal) return value.value;
|
|
@@ -2673,14 +2747,18 @@ function jsonErrorPosition(input) {
|
|
|
2673
2747
|
}
|
|
2674
2748
|
}
|
|
2675
2749
|
function isValidJsonText(input, flags = 0) {
|
|
2676
|
-
const
|
|
2750
|
+
const allowCanonical = flags === 0 || (flags & 1) !== 0;
|
|
2751
|
+
const allowJson5 = (flags & 2) !== 0;
|
|
2677
2752
|
try {
|
|
2678
2753
|
if (allowJson5) {
|
|
2679
2754
|
parseJsonText(input);
|
|
2680
2755
|
return true;
|
|
2681
2756
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2757
|
+
if (allowCanonical) {
|
|
2758
|
+
parseJsonText(input, { strictCanonical: true });
|
|
2759
|
+
return true;
|
|
2760
|
+
}
|
|
2761
|
+
return false;
|
|
2684
2762
|
} catch {
|
|
2685
2763
|
return false;
|
|
2686
2764
|
}
|
|
@@ -4209,7 +4287,11 @@ function applyModifier(date, modifier) {
|
|
|
4209
4287
|
const normalized = modifier.trim().toLowerCase();
|
|
4210
4288
|
if (normalized === "unixepoch") return new Date((date.getTime() / 864e5 + JULIAN_UNIX_EPOCH) * 1e3);
|
|
4211
4289
|
if (normalized === "utc" || normalized === "localtime") return result;
|
|
4212
|
-
|
|
4290
|
+
const weekday = /^weekday\s+([0-6])$/.exec(normalized);
|
|
4291
|
+
if (weekday) {
|
|
4292
|
+
const target = Number(weekday[1]);
|
|
4293
|
+
result.setUTCDate(result.getUTCDate() + (target - result.getUTCDay() + 7) % 7);
|
|
4294
|
+
} else if (normalized === "start of day") result.setUTCHours(0, 0, 0, 0);
|
|
4213
4295
|
else if (normalized === "start of month") {
|
|
4214
4296
|
result.setUTCDate(1);
|
|
4215
4297
|
result.setUTCHours(0, 0, 0, 0);
|
|
@@ -4667,13 +4749,15 @@ var mathFunctions = {
|
|
|
4667
4749
|
function escapeRegexChar(char) {
|
|
4668
4750
|
return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
|
|
4669
4751
|
}
|
|
4670
|
-
function likeLiteral(char) {
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4752
|
+
function likeLiteral(char, caseSensitive) {
|
|
4753
|
+
if (!caseSensitive) {
|
|
4754
|
+
const code = char.codePointAt(0);
|
|
4755
|
+
if (code >= 65 && code <= 90) return `[${char}${char.toLowerCase()}]`;
|
|
4756
|
+
if (code >= 97 && code <= 122) return `[${char}${char.toUpperCase()}]`;
|
|
4757
|
+
}
|
|
4674
4758
|
return escapeRegexChar(char);
|
|
4675
4759
|
}
|
|
4676
|
-
function likeMatch(text2, pattern, escape = null) {
|
|
4760
|
+
function likeMatch(text2, pattern, escape = null, caseSensitive = false) {
|
|
4677
4761
|
if (escape !== null && [...escape].length !== 1) {
|
|
4678
4762
|
throw new SqliteError("ESCAPE expression must be a single character", "other");
|
|
4679
4763
|
}
|
|
@@ -4683,13 +4767,13 @@ function likeMatch(text2, pattern, escape = null) {
|
|
|
4683
4767
|
const char = chars[i];
|
|
4684
4768
|
if (escape !== null && char === escape) {
|
|
4685
4769
|
const next = chars[++i];
|
|
4686
|
-
source += next === void 0 ? likeLiteral(char) : likeLiteral(next);
|
|
4770
|
+
source += next === void 0 ? likeLiteral(char, caseSensitive) : likeLiteral(next, caseSensitive);
|
|
4687
4771
|
} else if (char === "%") {
|
|
4688
4772
|
source += "[\\s\\S]*";
|
|
4689
4773
|
} else if (char === "_") {
|
|
4690
4774
|
source += "[\\s\\S]";
|
|
4691
4775
|
} else {
|
|
4692
|
-
source += likeLiteral(char);
|
|
4776
|
+
source += likeLiteral(char, caseSensitive);
|
|
4693
4777
|
}
|
|
4694
4778
|
}
|
|
4695
4779
|
return new RegExp(`${source}$`).test(text2);
|
|
@@ -4996,11 +5080,11 @@ var scalarFunctions = {
|
|
|
4996
5080
|
requireArgs3("load_extension", args, 1, 2);
|
|
4997
5081
|
throw new SqliteError("not authorized", "misuse");
|
|
4998
5082
|
},
|
|
4999
|
-
like(args) {
|
|
5083
|
+
like(args, context) {
|
|
5000
5084
|
requireArgs3("like", args, 2, 3);
|
|
5001
5085
|
if (args[0] === null || args[1] === null || args[2] === null) return null;
|
|
5002
5086
|
const escape = args[2] === void 0 ? null : text(args[2]);
|
|
5003
|
-
return likeMatch(text(args[1]), text(args[0]), escape) ? 1 : 0;
|
|
5087
|
+
return likeMatch(text(args[1]), text(args[0]), escape, context.caseSensitiveLike === true) ? 1 : 0;
|
|
5004
5088
|
},
|
|
5005
5089
|
glob(args) {
|
|
5006
5090
|
requireArgs3("glob", args, 2);
|
|
@@ -5345,7 +5429,8 @@ function sqlOr(left, right) {
|
|
|
5345
5429
|
if (leftTruth === null || rightTruth === null) return null;
|
|
5346
5430
|
return 0;
|
|
5347
5431
|
}
|
|
5348
|
-
function compareResult(op, left, right, collation) {
|
|
5432
|
+
function compareResult(op, left, right, collation, leftAffinity = null, rightAffinity = null) {
|
|
5433
|
+
[left, right] = applyComparisonAffinity(left, right, leftAffinity, rightAffinity);
|
|
5349
5434
|
if (op === "IS" || op === "IS NOT" || op === "IS DISTINCT FROM" || op === "IS NOT DISTINCT FROM") {
|
|
5350
5435
|
const equal = left === null || right === null ? left === right : (collation ? compareWithCollation(left, right, collation) : compareSql(left, right)) === 0;
|
|
5351
5436
|
if (op === "IS" || op === "IS NOT DISTINCT FROM") return booleanValue(equal);
|
|
@@ -5398,11 +5483,18 @@ function evalBinary(op, leftExpr, rightExpr, ctx) {
|
|
|
5398
5483
|
if (["=", "==", "!=", "<>", "<", "<=", ">", ">=", "IS", "IS NOT", "IS DISTINCT FROM", "IS NOT DISTINCT FROM"].includes(
|
|
5399
5484
|
op
|
|
5400
5485
|
)) {
|
|
5401
|
-
return compareResult(
|
|
5486
|
+
return compareResult(
|
|
5487
|
+
op,
|
|
5488
|
+
left,
|
|
5489
|
+
right,
|
|
5490
|
+
resolveComparisonCollation(leftExpr, rightExpr, ctx) ?? void 0,
|
|
5491
|
+
resolveComparisonAffinity(leftExpr, ctx),
|
|
5492
|
+
resolveComparisonAffinity(rightExpr, ctx)
|
|
5493
|
+
);
|
|
5402
5494
|
}
|
|
5403
5495
|
if (op === "LIKE" || op === "NOT LIKE" || op === "GLOB" || op === "NOT GLOB") {
|
|
5404
5496
|
if (left === null || right === null) return null;
|
|
5405
|
-
const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right)) : globMatch(textValue(left), textValue(right));
|
|
5497
|
+
const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right), null, ctx.functionContext?.caseSensitiveLike === true) : globMatch(textValue(left), textValue(right));
|
|
5406
5498
|
return booleanValue(op.startsWith("NOT") ? !matches : matches);
|
|
5407
5499
|
}
|
|
5408
5500
|
if (left === null || right === null) return null;
|
|
@@ -5577,14 +5669,26 @@ function inheritedCollation(expr, ctx) {
|
|
|
5577
5669
|
return null;
|
|
5578
5670
|
}
|
|
5579
5671
|
}
|
|
5580
|
-
function
|
|
5672
|
+
function resolveComparisonAffinity(expr, ctx) {
|
|
5673
|
+
switch (expr.type) {
|
|
5674
|
+
case "column":
|
|
5675
|
+
return ctx.resolveAffinity?.(expr.table, expr.name) ?? ctx.parent?.resolveAffinity?.(expr.table, expr.name) ?? null;
|
|
5676
|
+
case "cast":
|
|
5677
|
+
return affinityFromTypeName(expr.typeName);
|
|
5678
|
+
case "collate":
|
|
5679
|
+
return resolveComparisonAffinity(expr.expr, ctx);
|
|
5680
|
+
default:
|
|
5681
|
+
return null;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
function evalIn(left, values, not, leftAffinity) {
|
|
5581
5685
|
if (values.length === 0) return booleanValue(not);
|
|
5582
5686
|
if (left === null) return null;
|
|
5583
5687
|
let sawNull = false;
|
|
5584
5688
|
for (const value of values) {
|
|
5585
5689
|
if (value === null) {
|
|
5586
5690
|
sawNull = true;
|
|
5587
|
-
} else if (compareSql(left, value) === 0) {
|
|
5691
|
+
} else if (compareSql(...applyComparisonAffinity(left, value, leftAffinity, null)) === 0) {
|
|
5588
5692
|
return booleanValue(!not);
|
|
5589
5693
|
}
|
|
5590
5694
|
}
|
|
@@ -5630,8 +5734,9 @@ function evalExpr(expr, ctx) {
|
|
|
5630
5734
|
case "between": {
|
|
5631
5735
|
const value = evalExpr(expr.expr, ctx);
|
|
5632
5736
|
const collation = resolveComparisonCollation(expr.expr, expr.lower, ctx) ?? resolveComparisonCollation(expr.expr, expr.upper, ctx) ?? void 0;
|
|
5633
|
-
const
|
|
5634
|
-
const
|
|
5737
|
+
const affinity = resolveComparisonAffinity(expr.expr, ctx);
|
|
5738
|
+
const lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation, affinity);
|
|
5739
|
+
const result = sqlAnd(lower, () => compareResult("<=", value, evalExpr(expr.upper, ctx), collation, affinity));
|
|
5635
5740
|
if (result === null) return null;
|
|
5636
5741
|
return expr.not ? booleanValue(result === 0) : result;
|
|
5637
5742
|
}
|
|
@@ -5658,14 +5763,19 @@ function evalExpr(expr, ctx) {
|
|
|
5658
5763
|
}
|
|
5659
5764
|
const left = evalExpr(expr.expr, ctx);
|
|
5660
5765
|
const values = Array.isArray(expr.values) ? expr.values.map((value) => evalExpr(value, ctx)) : executeSelect(ctx, expr.values).rows.map((row) => row[0] ?? null);
|
|
5661
|
-
return evalIn(left, values, expr.not);
|
|
5766
|
+
return evalIn(left, values, expr.not, resolveComparisonAffinity(expr.expr, ctx));
|
|
5662
5767
|
}
|
|
5663
5768
|
case "like": {
|
|
5664
5769
|
const value = evalExpr(expr.expr, ctx);
|
|
5665
5770
|
const pattern = evalExpr(expr.pattern, ctx);
|
|
5666
5771
|
const escape = expr.escape === null ? null : evalExpr(expr.escape, ctx);
|
|
5667
5772
|
if (value === null || pattern === null || escape === null && expr.escape !== null) return null;
|
|
5668
|
-
const match = expr.op === "LIKE" ? likeMatch(
|
|
5773
|
+
const match = expr.op === "LIKE" ? likeMatch(
|
|
5774
|
+
textValue(value),
|
|
5775
|
+
textValue(pattern),
|
|
5776
|
+
escape === null ? null : textValue(escape),
|
|
5777
|
+
ctx.functionContext?.caseSensitiveLike === true
|
|
5778
|
+
) : globMatch(textValue(value), textValue(pattern));
|
|
5669
5779
|
return booleanValue(expr.not ? !match : match);
|
|
5670
5780
|
}
|
|
5671
5781
|
case "case": {
|
|
@@ -6806,7 +6916,7 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
6806
6916
|
insert(values, rowid) {
|
|
6807
6917
|
const command = this.detectCommand(values);
|
|
6808
6918
|
if (command !== null) {
|
|
6809
|
-
this.runCommand(command, values);
|
|
6919
|
+
this.runCommand(command, values, rowid);
|
|
6810
6920
|
return 0;
|
|
6811
6921
|
}
|
|
6812
6922
|
const assigned = rowid ?? this.nextRowid++;
|
|
@@ -6955,18 +7065,63 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
6955
7065
|
}
|
|
6956
7066
|
return parts.join(" ");
|
|
6957
7067
|
}
|
|
6958
|
-
/** FTS3 matchinfo
|
|
6959
|
-
matchinfo(cursor,
|
|
7068
|
+
/** FTS3 matchinfo format encoded as native-style 32-bit little-endian integers. */
|
|
7069
|
+
matchinfo(cursor, format = "pcx") {
|
|
6960
7070
|
const row = this.rows.get(cursor.rowid);
|
|
6961
7071
|
const nPhrase = Math.max(1, cursor.phraseTerms.length);
|
|
6962
7072
|
const nCol = this.columns.length;
|
|
6963
|
-
const values = [
|
|
6964
|
-
for (
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
7073
|
+
const values = [];
|
|
7074
|
+
for (const request of format) {
|
|
7075
|
+
switch (request) {
|
|
7076
|
+
case "p":
|
|
7077
|
+
values.push(nPhrase);
|
|
7078
|
+
break;
|
|
7079
|
+
case "c":
|
|
7080
|
+
values.push(nCol);
|
|
7081
|
+
break;
|
|
7082
|
+
case "s":
|
|
7083
|
+
for (let c = 0; c < nCol; c++) {
|
|
7084
|
+
let longest = 0;
|
|
7085
|
+
let run = 0;
|
|
7086
|
+
for (let p = 0; p < nPhrase; p++) {
|
|
7087
|
+
const phrase = cursor.phraseTerms[p] ?? [];
|
|
7088
|
+
if (row && this.phraseFreq(row, this.columns[c], phrase) > 0) {
|
|
7089
|
+
run++;
|
|
7090
|
+
longest = Math.max(longest, run);
|
|
7091
|
+
} else {
|
|
7092
|
+
run = 0;
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
values.push(longest);
|
|
7096
|
+
}
|
|
7097
|
+
break;
|
|
7098
|
+
case "x":
|
|
7099
|
+
for (let p = 0; p < nPhrase; p++) {
|
|
7100
|
+
const phrase = cursor.phraseTerms[p] ?? [];
|
|
7101
|
+
for (let c = 0; c < nCol; c++) {
|
|
7102
|
+
const column = this.columns[c];
|
|
7103
|
+
const localHits = row ? this.phraseFreq(row, column, phrase) : 0;
|
|
7104
|
+
let globalHits = 0;
|
|
7105
|
+
let matchingRows = 0;
|
|
7106
|
+
for (const candidate of this.rows.values()) {
|
|
7107
|
+
const hits = this.phraseFreq(candidate, column, phrase);
|
|
7108
|
+
globalHits += hits;
|
|
7109
|
+
if (hits > 0) matchingRows++;
|
|
7110
|
+
}
|
|
7111
|
+
values.push(localHits, globalHits, matchingRows);
|
|
7112
|
+
}
|
|
7113
|
+
}
|
|
7114
|
+
break;
|
|
7115
|
+
case "y":
|
|
7116
|
+
for (let p = 0; p < nPhrase; p++) {
|
|
7117
|
+
const phrase = cursor.phraseTerms[p] ?? [];
|
|
7118
|
+
for (let c = 0; c < nCol; c++) {
|
|
7119
|
+
values.push(row ? this.phraseFreq(row, this.columns[c], phrase) : 0);
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
break;
|
|
7123
|
+
default:
|
|
7124
|
+
throw new SqliteError(`unrecognized matchinfo request: ${request}`, "other");
|
|
6970
7125
|
}
|
|
6971
7126
|
}
|
|
6972
7127
|
const buf = new Uint8Array(values.length * 4);
|
|
@@ -7014,13 +7169,9 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7014
7169
|
if (!values.has(key)) return null;
|
|
7015
7170
|
const v = values.get(key);
|
|
7016
7171
|
if (typeof v !== "string") return null;
|
|
7017
|
-
const otherContent = [...values.entries()].some(
|
|
7018
|
-
([k, val]) => k !== key && val !== null && this.columns.some((c) => c.toLowerCase() === k)
|
|
7019
|
-
);
|
|
7020
|
-
if (otherContent) return null;
|
|
7021
7172
|
return v;
|
|
7022
7173
|
}
|
|
7023
|
-
runCommand(command, _values) {
|
|
7174
|
+
runCommand(command, _values, rowid) {
|
|
7024
7175
|
const cmd = command.toLowerCase();
|
|
7025
7176
|
if (cmd === "optimize") return;
|
|
7026
7177
|
if (cmd === "rebuild") {
|
|
@@ -7035,13 +7186,15 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7035
7186
|
"other"
|
|
7036
7187
|
);
|
|
7037
7188
|
}
|
|
7038
|
-
for (const
|
|
7189
|
+
for (const rowid2 of [...this.rows.keys()]) this.delete(rowid2);
|
|
7039
7190
|
return;
|
|
7040
7191
|
}
|
|
7041
7192
|
if (cmd.startsWith("merge=") || cmd.startsWith("automerge=")) {
|
|
7042
7193
|
throw new SqliteError("SQL logic error", "other");
|
|
7043
7194
|
}
|
|
7044
7195
|
if (cmd === "delete") {
|
|
7196
|
+
if (rowid === void 0) throw new SqliteError("SQL logic error", "other");
|
|
7197
|
+
this.delete(rowid);
|
|
7045
7198
|
return;
|
|
7046
7199
|
}
|
|
7047
7200
|
throw new SqliteError("SQL logic error", "other");
|
|
@@ -7274,11 +7427,6 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7274
7427
|
}
|
|
7275
7428
|
return n;
|
|
7276
7429
|
}
|
|
7277
|
-
termFreq(row, column, term) {
|
|
7278
|
-
const needle = this.normalizeQueryTerm(term);
|
|
7279
|
-
const tokens = row.tokensByColumn.get(column.toLowerCase()) ?? [];
|
|
7280
|
-
return tokens.filter((t) => t.term === needle || t.term.startsWith(needle)).length;
|
|
7281
|
-
}
|
|
7282
7430
|
docLength(row) {
|
|
7283
7431
|
let n = 0;
|
|
7284
7432
|
for (const col of this.indexedColumns()) n += (row.tokensByColumn.get(col.toLowerCase()) ?? []).length;
|
|
@@ -7542,6 +7690,8 @@ var Table = class _Table {
|
|
|
7542
7690
|
scanCache = null;
|
|
7543
7691
|
/** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
|
|
7544
7692
|
equalityHashes = null;
|
|
7693
|
+
/** Cached maximum rowid. `undefined` means recompute after deleting the maximum. */
|
|
7694
|
+
maximumRowid = null;
|
|
7545
7695
|
frozen = false;
|
|
7546
7696
|
constructor(name, columns, options = {}) {
|
|
7547
7697
|
this.name = name;
|
|
@@ -7744,7 +7894,12 @@ var Table = class _Table {
|
|
|
7744
7894
|
if (alias) values.set(normalizeColumnName(alias.name), targetKey);
|
|
7745
7895
|
const candidate = { rowid: targetKey, values };
|
|
7746
7896
|
this.validate(candidate, key);
|
|
7747
|
-
if (targetKey !== key)
|
|
7897
|
+
if (targetKey !== key) {
|
|
7898
|
+
this.rows.delete(key);
|
|
7899
|
+
if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
|
|
7900
|
+
this.maximumRowid = void 0;
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7748
7903
|
this.rows.set(targetKey, candidate);
|
|
7749
7904
|
this.advanceNextRowid(targetKey);
|
|
7750
7905
|
this.reindexEquality(existing, candidate);
|
|
@@ -7758,6 +7913,9 @@ var Table = class _Table {
|
|
|
7758
7913
|
if (this.withoutRowid) this.clusteredRows.delete(this.makeClusterKey(existing.values));
|
|
7759
7914
|
this.unindexEquality(existing);
|
|
7760
7915
|
this.invalidateScan();
|
|
7916
|
+
if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
|
|
7917
|
+
this.maximumRowid = void 0;
|
|
7918
|
+
}
|
|
7761
7919
|
return this.rows.delete(key);
|
|
7762
7920
|
}
|
|
7763
7921
|
*scan() {
|
|
@@ -7779,6 +7937,7 @@ var Table = class _Table {
|
|
|
7779
7937
|
strict: this.strict
|
|
7780
7938
|
});
|
|
7781
7939
|
copy.nextRowid = this.nextRowid;
|
|
7940
|
+
copy.maximumRowid = this.maximumRowid;
|
|
7782
7941
|
for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
|
|
7783
7942
|
for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
|
|
7784
7943
|
return copy;
|
|
@@ -7791,6 +7950,7 @@ var Table = class _Table {
|
|
|
7791
7950
|
}
|
|
7792
7951
|
/** Rebuild clustered storage after snapshot decode or bulk load. */
|
|
7793
7952
|
rebuildClusteredRows() {
|
|
7953
|
+
this.maximumRowid = void 0;
|
|
7794
7954
|
if (!this.withoutRowid) return;
|
|
7795
7955
|
this.clusteredRows.clear();
|
|
7796
7956
|
for (const row of this.rows.values()) {
|
|
@@ -7899,11 +8059,23 @@ var Table = class _Table {
|
|
|
7899
8059
|
return primary[0];
|
|
7900
8060
|
}
|
|
7901
8061
|
allocateRowid() {
|
|
8062
|
+
if (!this.columns.some((column) => column.autoincrement)) {
|
|
8063
|
+
if (this.maximumRowid === void 0) {
|
|
8064
|
+
this.maximumRowid = null;
|
|
8065
|
+
for (const rowid of this.rows.keys()) {
|
|
8066
|
+
if (this.maximumRowid === null || compareRowids(rowid, this.maximumRowid) > 0) this.maximumRowid = rowid;
|
|
8067
|
+
}
|
|
8068
|
+
}
|
|
8069
|
+
return this.maximumRowid === null ? 1 : incrementRowid(this.maximumRowid);
|
|
8070
|
+
}
|
|
7902
8071
|
let candidate = canonicalRowid(this.nextRowid);
|
|
7903
8072
|
while (this.rows.has(candidate)) candidate = incrementRowid(candidate);
|
|
7904
8073
|
return candidate;
|
|
7905
8074
|
}
|
|
7906
8075
|
advanceNextRowid(rowid) {
|
|
8076
|
+
if (this.maximumRowid === null || this.maximumRowid !== void 0 && compareRowids(rowid, this.maximumRowid) > 0) {
|
|
8077
|
+
this.maximumRowid = rowid;
|
|
8078
|
+
}
|
|
7907
8079
|
if (compareRowids(rowid, this.nextRowid) >= 0) this.nextRowid = incrementRowid(rowid);
|
|
7908
8080
|
}
|
|
7909
8081
|
};
|
|
@@ -7993,6 +8165,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
7993
8165
|
changes = 0;
|
|
7994
8166
|
totalChanges = 0;
|
|
7995
8167
|
foreignKeysEnabled = false;
|
|
8168
|
+
/** When true, LIKE / like() are case-sensitive (SQLite `PRAGMA case_sensitive_like`). */
|
|
8169
|
+
caseSensitiveLike = false;
|
|
7996
8170
|
schemaVersion = 0;
|
|
7997
8171
|
userVersion = 0;
|
|
7998
8172
|
databaseForSchema(schema, qualifiedForError) {
|
|
@@ -8437,6 +8611,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8437
8611
|
copy.changes = this.changes;
|
|
8438
8612
|
copy.totalChanges = this.totalChanges;
|
|
8439
8613
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8614
|
+
copy.caseSensitiveLike = this.caseSensitiveLike;
|
|
8440
8615
|
copy.schemaVersion = this.schemaVersion;
|
|
8441
8616
|
copy.userVersion = this.userVersion;
|
|
8442
8617
|
return copy;
|
|
@@ -8456,6 +8631,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8456
8631
|
copy.changes = this.changes;
|
|
8457
8632
|
copy.totalChanges = this.totalChanges;
|
|
8458
8633
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8634
|
+
copy.caseSensitiveLike = this.caseSensitiveLike;
|
|
8459
8635
|
copy.schemaVersion = this.schemaVersion;
|
|
8460
8636
|
copy.userVersion = this.userVersion;
|
|
8461
8637
|
return copy;
|
|
@@ -8517,6 +8693,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8517
8693
|
this.changes = copy.changes;
|
|
8518
8694
|
this.totalChanges = copy.totalChanges;
|
|
8519
8695
|
this.foreignKeysEnabled = copy.foreignKeysEnabled;
|
|
8696
|
+
this.caseSensitiveLike = copy.caseSensitiveLike;
|
|
8520
8697
|
this.schemaVersion = copy.schemaVersion;
|
|
8521
8698
|
this.userVersion = copy.userVersion;
|
|
8522
8699
|
}
|
|
@@ -9019,8 +9196,9 @@ var ExecutionEnv = class {
|
|
|
9019
9196
|
createEvalContext(row = null, parent) {
|
|
9020
9197
|
const scope = row ?? this.triggerScope;
|
|
9021
9198
|
const cells = scope?.cells ?? [];
|
|
9199
|
+
const inheritedParent = parent ?? (row !== null && this.triggerScope !== null && row !== this.triggerScope ? this.createEvalContext(this.triggerScope) : void 0);
|
|
9022
9200
|
const context = {
|
|
9023
|
-
parent,
|
|
9201
|
+
parent: inheritedParent,
|
|
9024
9202
|
ftsMatch: scope?.ftsMatch ?? null,
|
|
9025
9203
|
functions: this.functions,
|
|
9026
9204
|
functionContext: {
|
|
@@ -9030,6 +9208,7 @@ var ExecutionEnv = class {
|
|
|
9030
9208
|
now: this.hooks.now,
|
|
9031
9209
|
random: this.hooks.random,
|
|
9032
9210
|
randomU64: this.hooks.randomU64,
|
|
9211
|
+
caseSensitiveLike: this.state.caseSensitiveLike,
|
|
9033
9212
|
ftsMatch: scope?.ftsMatch ?? null,
|
|
9034
9213
|
ftsRowid: scope?.rowid,
|
|
9035
9214
|
ftsSourceTable: scope?.sourceTable,
|
|
@@ -9088,6 +9267,17 @@ var ExecutionEnv = class {
|
|
|
9088
9267
|
const cell = matches[0];
|
|
9089
9268
|
return cell.affinity === "REAL" && typeof cell.value === "number" ? "real" : storageClassOf(cell.value);
|
|
9090
9269
|
},
|
|
9270
|
+
resolveAffinity: (table, name) => {
|
|
9271
|
+
const key = name.toLowerCase();
|
|
9272
|
+
const tableKey = table?.toLowerCase();
|
|
9273
|
+
const matches = cells.filter((cell) => {
|
|
9274
|
+
if ((cell.nameLower ?? cell.name.toLowerCase()) !== key) return false;
|
|
9275
|
+
if (table === null) return !cell.hiddenByUsing;
|
|
9276
|
+
return (cell.tableLower ?? cell.table?.toLowerCase()) === tableKey;
|
|
9277
|
+
});
|
|
9278
|
+
if (matches.length !== 1) return null;
|
|
9279
|
+
return matches[0].affinity ?? null;
|
|
9280
|
+
},
|
|
9091
9281
|
resolveCollation: (table, name) => {
|
|
9092
9282
|
const key = name.toLowerCase();
|
|
9093
9283
|
const tableKey = table?.toLowerCase();
|
|
@@ -9495,6 +9685,8 @@ function queryPragma(name, args, env) {
|
|
|
9495
9685
|
switch (key) {
|
|
9496
9686
|
case "foreign_keys":
|
|
9497
9687
|
return single("foreign_keys", env.state.foreignKeysEnabled ? 1 : 0);
|
|
9688
|
+
case "case_sensitive_like":
|
|
9689
|
+
return single("case_sensitive_like", env.state.caseSensitiveLike ? 1 : 0);
|
|
9498
9690
|
case "user_version":
|
|
9499
9691
|
return single("user_version", env.state.userVersion);
|
|
9500
9692
|
case "schema_version":
|
|
@@ -9922,83 +10114,6 @@ function hasTableValuedFunction(name) {
|
|
|
9922
10114
|
return hasRegisteredTableValuedFunction(name);
|
|
9923
10115
|
}
|
|
9924
10116
|
|
|
9925
|
-
// src/schema/catalog.ts
|
|
9926
|
-
function schemaCatalogRows(state) {
|
|
9927
|
-
const tables = [...state.tables.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9928
|
-
const indexes = [...state.indexes.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9929
|
-
const views = [...state.views.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9930
|
-
const triggers = [...state.triggers.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9931
|
-
const rows = [];
|
|
9932
|
-
let rootpage = 2;
|
|
9933
|
-
for (const table of tables) {
|
|
9934
|
-
rows.push({
|
|
9935
|
-
type: "table",
|
|
9936
|
-
name: table.name,
|
|
9937
|
-
tbl_name: table.name,
|
|
9938
|
-
rootpage: rootpage++,
|
|
9939
|
-
sql: table.originalSql
|
|
9940
|
-
});
|
|
9941
|
-
}
|
|
9942
|
-
for (const index of indexes) {
|
|
9943
|
-
rows.push({
|
|
9944
|
-
type: "index",
|
|
9945
|
-
name: index.name,
|
|
9946
|
-
tbl_name: index.tableName,
|
|
9947
|
-
rootpage: rootpage++,
|
|
9948
|
-
sql: index.originalSql
|
|
9949
|
-
});
|
|
9950
|
-
}
|
|
9951
|
-
for (const view of views) {
|
|
9952
|
-
rows.push({
|
|
9953
|
-
type: "view",
|
|
9954
|
-
name: view.name,
|
|
9955
|
-
tbl_name: view.name,
|
|
9956
|
-
rootpage: 0,
|
|
9957
|
-
sql: view.originalSql
|
|
9958
|
-
});
|
|
9959
|
-
}
|
|
9960
|
-
for (const trigger of triggers) {
|
|
9961
|
-
rows.push({
|
|
9962
|
-
type: "trigger",
|
|
9963
|
-
name: trigger.name,
|
|
9964
|
-
tbl_name: trigger.tableName,
|
|
9965
|
-
rootpage: 0,
|
|
9966
|
-
sql: trigger.originalSql
|
|
9967
|
-
});
|
|
9968
|
-
}
|
|
9969
|
-
return rows;
|
|
9970
|
-
}
|
|
9971
|
-
function compareNames2(a, b) {
|
|
9972
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
9973
|
-
}
|
|
9974
|
-
function buildSchemaCatalog(state, name = "sqlite_schema") {
|
|
9975
|
-
const catalog = new Table(name, [
|
|
9976
|
-
makeColumnInfo("type", "TEXT"),
|
|
9977
|
-
makeColumnInfo("name", "TEXT"),
|
|
9978
|
-
makeColumnInfo("tbl_name", "TEXT"),
|
|
9979
|
-
makeColumnInfo("rootpage", "INTEGER"),
|
|
9980
|
-
makeColumnInfo("sql", "TEXT")
|
|
9981
|
-
]);
|
|
9982
|
-
for (const row of schemaCatalogRows(state)) {
|
|
9983
|
-
catalog.insert({
|
|
9984
|
-
values: {
|
|
9985
|
-
type: row.type,
|
|
9986
|
-
name: row.name,
|
|
9987
|
-
tbl_name: row.tbl_name,
|
|
9988
|
-
rootpage: row.rootpage,
|
|
9989
|
-
sql: row.sql
|
|
9990
|
-
}
|
|
9991
|
-
});
|
|
9992
|
-
}
|
|
9993
|
-
return catalog;
|
|
9994
|
-
}
|
|
9995
|
-
function buildSqliteSchema(state) {
|
|
9996
|
-
return buildSchemaCatalog(state, "sqlite_schema");
|
|
9997
|
-
}
|
|
9998
|
-
function buildSqliteMaster(state) {
|
|
9999
|
-
return buildSchemaCatalog(state, "sqlite_master");
|
|
10000
|
-
}
|
|
10001
|
-
|
|
10002
10117
|
// src/expressions/equals.ts
|
|
10003
10118
|
function exprEquals(left, right) {
|
|
10004
10119
|
if (left.type !== right.type) return false;
|
|
@@ -10392,6 +10507,83 @@ function columnOnTable(column, alias, tableName) {
|
|
|
10392
10507
|
return column.table !== null && matchesTable(column.table, alias, tableName);
|
|
10393
10508
|
}
|
|
10394
10509
|
|
|
10510
|
+
// src/schema/catalog.ts
|
|
10511
|
+
function schemaCatalogRows(state) {
|
|
10512
|
+
const tables = [...state.tables.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10513
|
+
const indexes = [...state.indexes.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10514
|
+
const views = [...state.views.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10515
|
+
const triggers = [...state.triggers.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10516
|
+
const rows = [];
|
|
10517
|
+
let rootpage = 2;
|
|
10518
|
+
for (const table of tables) {
|
|
10519
|
+
rows.push({
|
|
10520
|
+
type: "table",
|
|
10521
|
+
name: table.name,
|
|
10522
|
+
tbl_name: table.name,
|
|
10523
|
+
rootpage: rootpage++,
|
|
10524
|
+
sql: table.originalSql
|
|
10525
|
+
});
|
|
10526
|
+
}
|
|
10527
|
+
for (const index of indexes) {
|
|
10528
|
+
rows.push({
|
|
10529
|
+
type: "index",
|
|
10530
|
+
name: index.name,
|
|
10531
|
+
tbl_name: index.tableName,
|
|
10532
|
+
rootpage: rootpage++,
|
|
10533
|
+
sql: index.originalSql
|
|
10534
|
+
});
|
|
10535
|
+
}
|
|
10536
|
+
for (const view of views) {
|
|
10537
|
+
rows.push({
|
|
10538
|
+
type: "view",
|
|
10539
|
+
name: view.name,
|
|
10540
|
+
tbl_name: view.name,
|
|
10541
|
+
rootpage: 0,
|
|
10542
|
+
sql: view.originalSql
|
|
10543
|
+
});
|
|
10544
|
+
}
|
|
10545
|
+
for (const trigger of triggers) {
|
|
10546
|
+
rows.push({
|
|
10547
|
+
type: "trigger",
|
|
10548
|
+
name: trigger.name,
|
|
10549
|
+
tbl_name: trigger.tableName,
|
|
10550
|
+
rootpage: 0,
|
|
10551
|
+
sql: trigger.originalSql
|
|
10552
|
+
});
|
|
10553
|
+
}
|
|
10554
|
+
return rows;
|
|
10555
|
+
}
|
|
10556
|
+
function compareNames2(a, b) {
|
|
10557
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
10558
|
+
}
|
|
10559
|
+
function buildSchemaCatalog(state, name = "sqlite_schema") {
|
|
10560
|
+
const catalog = new Table(name, [
|
|
10561
|
+
makeColumnInfo("type", "TEXT"),
|
|
10562
|
+
makeColumnInfo("name", "TEXT"),
|
|
10563
|
+
makeColumnInfo("tbl_name", "TEXT"),
|
|
10564
|
+
makeColumnInfo("rootpage", "INTEGER"),
|
|
10565
|
+
makeColumnInfo("sql", "TEXT")
|
|
10566
|
+
]);
|
|
10567
|
+
for (const row of schemaCatalogRows(state)) {
|
|
10568
|
+
catalog.insert({
|
|
10569
|
+
values: {
|
|
10570
|
+
type: row.type,
|
|
10571
|
+
name: row.name,
|
|
10572
|
+
tbl_name: row.tbl_name,
|
|
10573
|
+
rootpage: row.rootpage,
|
|
10574
|
+
sql: row.sql
|
|
10575
|
+
}
|
|
10576
|
+
});
|
|
10577
|
+
}
|
|
10578
|
+
return catalog;
|
|
10579
|
+
}
|
|
10580
|
+
function buildSqliteSchema(state) {
|
|
10581
|
+
return buildSchemaCatalog(state, "sqlite_schema");
|
|
10582
|
+
}
|
|
10583
|
+
function buildSqliteMaster(state) {
|
|
10584
|
+
return buildSchemaCatalog(state, "sqlite_master");
|
|
10585
|
+
}
|
|
10586
|
+
|
|
10395
10587
|
// src/executor/simple-select.ts
|
|
10396
10588
|
function tryExecuteSimpleSelect(stmt, env) {
|
|
10397
10589
|
if (stmt.with || stmt.compound || stmt.distinct || stmt.groupBy.length > 0 || stmt.having || stmt.windows.length > 0)
|
|
@@ -10440,7 +10632,9 @@ function tryExecuteSimpleSelect(stmt, env) {
|
|
|
10440
10632
|
} catch {
|
|
10441
10633
|
return null;
|
|
10442
10634
|
}
|
|
10443
|
-
|
|
10635
|
+
const column = eq.column.toLowerCase();
|
|
10636
|
+
const affinity = isRowidName2(column) ? "INTEGER" : table.columns.find((item) => (item.nameLower ?? item.name.toLowerCase()) === column)?.affinity;
|
|
10637
|
+
filters.push({ column, value: affinity ? applyAffinity(value, affinity) : value });
|
|
10444
10638
|
}
|
|
10445
10639
|
}
|
|
10446
10640
|
let limit = env.maxRows;
|
|
@@ -10622,7 +10816,7 @@ function executeSelect2(stmt, env, parent) {
|
|
|
10622
10816
|
}
|
|
10623
10817
|
const savedCtes = new Map(env.ctes);
|
|
10624
10818
|
try {
|
|
10625
|
-
if (stmt.with) executeWith(stmt, env, parent);
|
|
10819
|
+
if (stmt.with) executeWith(stmt.with, env, parent);
|
|
10626
10820
|
const base = executeSelectCore(
|
|
10627
10821
|
{
|
|
10628
10822
|
...stmt,
|
|
@@ -10678,22 +10872,39 @@ function executeSelect2(stmt, env, parent) {
|
|
|
10678
10872
|
for (const [name, result] of savedCtes) env.ctes.set(name, result);
|
|
10679
10873
|
}
|
|
10680
10874
|
}
|
|
10681
|
-
function executeWith(
|
|
10682
|
-
for (const cte of
|
|
10875
|
+
function executeWith(withClause, env, parent) {
|
|
10876
|
+
for (const cte of withClause.ctes) {
|
|
10683
10877
|
const key = cte.name.toLowerCase();
|
|
10684
|
-
if (
|
|
10878
|
+
if (withClause.recursive && referencesTable(cte.select, cte.name) && cte.select.compound) {
|
|
10685
10879
|
const anchor = executeSelect2({ ...cte.select, compound: null }, env, parent);
|
|
10686
10880
|
const columns = cte.columns ?? anchor.columns;
|
|
10687
|
-
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10881
|
+
const recursive = cte.select.compound.select;
|
|
10882
|
+
const limit = recursive.limit ? toInteger(evalExpr(recursive.limit.limit, env.createEvalContext(null, parent))) : null;
|
|
10883
|
+
if (recursive.limit && limit === null) throw new SqliteError("datatype mismatch", "datatype_mismatch");
|
|
10884
|
+
const maxRows = limit === null || Number(limit) < 0 ? Number.POSITIVE_INFINITY : Number(limit);
|
|
10885
|
+
const queue = resultValues(anchor);
|
|
10886
|
+
const discovered = [...queue];
|
|
10887
|
+
const accumulated = [];
|
|
10888
|
+
if (recursive.orderBy.length > 0) {
|
|
10889
|
+
queue.sort((left, right) => compareCteQueueRows(left, right, columns, recursive.orderBy, env, parent));
|
|
10890
|
+
}
|
|
10891
|
+
while (queue.length > 0 && accumulated.length < maxRows) {
|
|
10892
|
+
const current = queue.shift();
|
|
10893
|
+
accumulated.push(current);
|
|
10894
|
+
env.ctes.set(key, valuesToResult(columns, [current]));
|
|
10895
|
+
const nextResult = executeSelect2({ ...recursive, orderBy: [], limit: null }, env, parent);
|
|
10692
10896
|
const candidates = resultValues(nextResult);
|
|
10693
|
-
const additions =
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10897
|
+
const additions = [];
|
|
10898
|
+
for (const candidate of candidates) {
|
|
10899
|
+
if (cte.select.compound.op !== "UNION ALL" && [...discovered, ...additions].some((existing) => rowsEqual2(existing, candidate)))
|
|
10900
|
+
continue;
|
|
10901
|
+
additions.push(candidate);
|
|
10902
|
+
}
|
|
10903
|
+
discovered.push(...additions);
|
|
10904
|
+
queue.push(...additions);
|
|
10905
|
+
if (recursive.orderBy.length > 0) {
|
|
10906
|
+
queue.sort((left, right) => compareCteQueueRows(left, right, columns, recursive.orderBy, env, parent));
|
|
10907
|
+
}
|
|
10697
10908
|
}
|
|
10698
10909
|
env.ctes.set(key, valuesToResult(columns, accumulated));
|
|
10699
10910
|
} else {
|
|
@@ -10703,6 +10914,26 @@ function executeWith(stmt, env, parent) {
|
|
|
10703
10914
|
}
|
|
10704
10915
|
}
|
|
10705
10916
|
}
|
|
10917
|
+
function compareCteQueueRows(left, right, columns, order, env, parent) {
|
|
10918
|
+
const scope = (values) => ({
|
|
10919
|
+
cells: columns.map((name, index) => ({ table: null, name, value: values[index] ?? null }))
|
|
10920
|
+
});
|
|
10921
|
+
const leftScope = scope(left);
|
|
10922
|
+
const rightScope = scope(right);
|
|
10923
|
+
for (const item of order) {
|
|
10924
|
+
if (item.expr.type === "literal" && typeof item.expr.value === "number" && Number.isInteger(item.expr.value)) {
|
|
10925
|
+
const index = item.expr.value - 1;
|
|
10926
|
+
const result2 = compareNullable(left[index] ?? null, right[index] ?? null, item);
|
|
10927
|
+
if (result2 !== 0) return result2;
|
|
10928
|
+
continue;
|
|
10929
|
+
}
|
|
10930
|
+
const a = evalExpr(item.expr, env.createEvalContext(leftScope, parent));
|
|
10931
|
+
const b = evalExpr(item.expr, env.createEvalContext(rightScope, parent));
|
|
10932
|
+
const result = compareNullable(a, b, item, leftScope);
|
|
10933
|
+
if (result !== 0) return result;
|
|
10934
|
+
}
|
|
10935
|
+
return 0;
|
|
10936
|
+
}
|
|
10706
10937
|
function executeSelectCore(stmt, env, parent) {
|
|
10707
10938
|
let scopes;
|
|
10708
10939
|
let skipWhere = false;
|
|
@@ -10758,10 +10989,18 @@ function executeSelectCore(stmt, env, parent) {
|
|
|
10758
10989
|
}
|
|
10759
10990
|
}
|
|
10760
10991
|
const groupBy = stmt.groupBy.map((expr) => {
|
|
10761
|
-
if (expr.type
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
10992
|
+
if (expr.type === "literal" && typeof expr.value === "number" && Number.isInteger(expr.value)) {
|
|
10993
|
+
const column = stmt.columns[expr.value - 1];
|
|
10994
|
+
if (column?.type !== "expr") throw new SqliteError(`${expr.value}th GROUP BY term out of range`, "other");
|
|
10995
|
+
return column.expr;
|
|
10996
|
+
}
|
|
10997
|
+
if (expr.type === "column" && expr.table === null) {
|
|
10998
|
+
const column = stmt.columns.find(
|
|
10999
|
+
(candidate) => candidate.type === "expr" && candidate.alias?.toLowerCase() === expr.name.toLowerCase()
|
|
11000
|
+
);
|
|
11001
|
+
if (column?.type === "expr") return column.expr;
|
|
11002
|
+
}
|
|
11003
|
+
return expr;
|
|
10765
11004
|
});
|
|
10766
11005
|
const groups = aggregate ? groupRows(scopes, groupBy, env, parent) : scopes.map((scope) => [scope]);
|
|
10767
11006
|
const windowScopes = aggregate ? groups.map((group) => group[0] ?? { cells: [] }) : scopes;
|
|
@@ -11218,7 +11457,10 @@ function groupRows(rows, expressions, env, parent) {
|
|
|
11218
11457
|
const groups = [];
|
|
11219
11458
|
const indexByKey = /* @__PURE__ */ new Map();
|
|
11220
11459
|
for (const row of rows) {
|
|
11221
|
-
const keyValues = expressions.map((expr) =>
|
|
11460
|
+
const keyValues = expressions.map((expr) => {
|
|
11461
|
+
const value = evalExpr(expr, env.createEvalContext(row, parent));
|
|
11462
|
+
return expr.type === "collate" ? normalizeForCollation(value, expr.collation) : value;
|
|
11463
|
+
});
|
|
11222
11464
|
const key = valueKey(keyValues);
|
|
11223
11465
|
const index = indexByKey.get(key);
|
|
11224
11466
|
if (index === void 0) {
|
|
@@ -11241,7 +11483,8 @@ function aggregateValue(expr, rows, env, parent) {
|
|
|
11241
11483
|
const accumulator = env.functions.createAggregate(expr.name);
|
|
11242
11484
|
if (!accumulator) throw new SqliteError(`no such aggregate function: ${expr.name}`, "other");
|
|
11243
11485
|
const seen = [];
|
|
11244
|
-
|
|
11486
|
+
const orderedRows = expr.orderBy.length > 0 ? [...rows].sort((a, b) => compareScopes(a, b, expr.orderBy, env, parent)) : rows;
|
|
11487
|
+
for (const row of orderedRows) {
|
|
11245
11488
|
const ctx = env.createEvalContext(row, parent);
|
|
11246
11489
|
if (expr.filter && isTruthySql(evalExpr(expr.filter, ctx)) !== true) continue;
|
|
11247
11490
|
const args = expr.args === "*" ? [] : expr.args.map((arg) => evalExpr(arg, ctx));
|
|
@@ -11357,8 +11600,10 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
|
11357
11600
|
if (!spec.frame) return [0, spec.orderBy.length > 0 ? defaultFrameEnd : Math.max(0, length - 1)];
|
|
11358
11601
|
const isRangeLike = spec.frame.type === "RANGE" || spec.frame.type === "GROUPS";
|
|
11359
11602
|
let peerFirst = index;
|
|
11603
|
+
let peerLast = index;
|
|
11360
11604
|
if (isRangeLike && spec.orderBy.length > 0) {
|
|
11361
11605
|
while (peerFirst > 0 && rowsEqual2(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
|
|
11606
|
+
while (peerLast + 1 < length && rowsEqual2(orderKeys[peerLast], orderKeys[peerLast + 1])) peerLast++;
|
|
11362
11607
|
}
|
|
11363
11608
|
const bound = (item, isStart) => {
|
|
11364
11609
|
switch (item.kind) {
|
|
@@ -11367,12 +11612,54 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
|
11367
11612
|
case "unbounded_following":
|
|
11368
11613
|
return Math.max(0, length - 1);
|
|
11369
11614
|
case "current_row":
|
|
11370
|
-
if (isRangeLike) return isStart ? peerFirst :
|
|
11615
|
+
if (isRangeLike) return isStart ? peerFirst : peerLast;
|
|
11371
11616
|
return index;
|
|
11372
11617
|
case "preceding":
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11618
|
+
case "following": {
|
|
11619
|
+
const offset = Number(evalExpr(item.expr, ctx));
|
|
11620
|
+
if (spec.frame?.type === "GROUPS") {
|
|
11621
|
+
const groups = [];
|
|
11622
|
+
for (let i = 0; i < length; ) {
|
|
11623
|
+
let last = i;
|
|
11624
|
+
while (last + 1 < length && rowsEqual2(orderKeys[last], orderKeys[last + 1])) last++;
|
|
11625
|
+
groups.push({ first: i, last });
|
|
11626
|
+
i = last + 1;
|
|
11627
|
+
}
|
|
11628
|
+
const currentGroup = groups.findIndex((group) => index >= group.first && index <= group.last);
|
|
11629
|
+
const delta = item.kind === "preceding" ? -offset : offset;
|
|
11630
|
+
const target = groups[Math.max(0, Math.min(groups.length - 1, currentGroup + delta))];
|
|
11631
|
+
return isStart ? target.first : target.last;
|
|
11632
|
+
}
|
|
11633
|
+
if (spec.frame?.type === "RANGE" && spec.orderBy.length === 1) {
|
|
11634
|
+
const rawCurrent = orderKeys[index]?.[0];
|
|
11635
|
+
const current = typeof rawCurrent === "number" ? rawCurrent : typeof rawCurrent === "bigint" ? Number(rawCurrent) : rawCurrent && typeof rawCurrent === "object" && "value" in rawCurrent ? Number(rawCurrent.value) : Number.NaN;
|
|
11636
|
+
if (!Number.isFinite(current) || !Number.isFinite(offset)) return isStart ? peerFirst : peerLast;
|
|
11637
|
+
const descending = spec.orderBy[0]?.dir === "DESC";
|
|
11638
|
+
const signed = item.kind === "preceding" ? -offset : offset;
|
|
11639
|
+
const target = current + (descending ? -signed : signed);
|
|
11640
|
+
const numericKey = (position) => {
|
|
11641
|
+
const value = orderKeys[position]?.[0];
|
|
11642
|
+
if (typeof value === "number") return value;
|
|
11643
|
+
if (typeof value === "bigint") return Number(value);
|
|
11644
|
+
if (value && typeof value === "object" && "value" in value) return Number(value.value);
|
|
11645
|
+
return Number.NaN;
|
|
11646
|
+
};
|
|
11647
|
+
if (isStart) {
|
|
11648
|
+
for (let i = 0; i < length; i++) {
|
|
11649
|
+
const value = numericKey(i);
|
|
11650
|
+
if (descending && value <= target || !descending && value >= target) return i;
|
|
11651
|
+
}
|
|
11652
|
+
return length;
|
|
11653
|
+
}
|
|
11654
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
11655
|
+
const value = numericKey(i);
|
|
11656
|
+
if (descending && value >= target || !descending && value <= target) return i;
|
|
11657
|
+
}
|
|
11658
|
+
return -1;
|
|
11659
|
+
}
|
|
11660
|
+
const rowOffset = Number(toInteger(evalExpr(item.expr, ctx)) ?? 0);
|
|
11661
|
+
return item.kind === "preceding" ? Math.max(0, index - rowOffset) : Math.min(Math.max(0, length - 1), index + rowOffset);
|
|
11662
|
+
}
|
|
11376
11663
|
}
|
|
11377
11664
|
};
|
|
11378
11665
|
return [bound(spec.frame.start, true), bound(spec.frame.end, false)];
|
|
@@ -11999,6 +12286,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
|
|
|
11999
12286
|
}
|
|
12000
12287
|
env.triggerDepth++;
|
|
12001
12288
|
const savedScope = env.triggerScope;
|
|
12289
|
+
const savedLastInsertRowid = env.state.lastInsertRowid;
|
|
12002
12290
|
env.triggerScope = triggerScope(table, oldRow, newValues);
|
|
12003
12291
|
try {
|
|
12004
12292
|
for (const statement of trigger.body) {
|
|
@@ -12016,6 +12304,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
|
|
|
12016
12304
|
}
|
|
12017
12305
|
throw error;
|
|
12018
12306
|
} finally {
|
|
12307
|
+
env.state.lastInsertRowid = savedLastInsertRowid;
|
|
12019
12308
|
env.triggerScope = savedScope;
|
|
12020
12309
|
env.triggerDepth--;
|
|
12021
12310
|
}
|
|
@@ -12110,9 +12399,20 @@ function storeColumnValue(table, column, value) {
|
|
|
12110
12399
|
return applyAffinity(value, column.affinity);
|
|
12111
12400
|
}
|
|
12112
12401
|
function executeInsert(stmt, env) {
|
|
12402
|
+
try {
|
|
12403
|
+
return withDmlCtes(stmt.with, env, () => executeInsertCore(stmt, env));
|
|
12404
|
+
} catch (error) {
|
|
12405
|
+
handleConflictRollback(stmt.mode === "insert_or_rollback", error, env);
|
|
12406
|
+
throw error;
|
|
12407
|
+
}
|
|
12408
|
+
}
|
|
12409
|
+
function executeInsertCore(stmt, env) {
|
|
12113
12410
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12114
12411
|
return executeVirtualInsert(stmt, env);
|
|
12115
12412
|
}
|
|
12413
|
+
const totalBefore = env.state.totalChanges;
|
|
12414
|
+
const view = writableView(stmt.table, "INSERT", env);
|
|
12415
|
+
if (view) return executeViewInsert(stmt, view, env, totalBefore);
|
|
12116
12416
|
const fast = tryFastInsert(stmt, env);
|
|
12117
12417
|
if (fast) return fast;
|
|
12118
12418
|
const table = env.state.getWritableTable(stmt.table);
|
|
@@ -12130,11 +12430,14 @@ function executeInsert(stmt, env) {
|
|
|
12130
12430
|
const suppliedIndexes = table.columns.map(
|
|
12131
12431
|
(column) => columnNames.findIndex((name) => name.toLowerCase() === (column.nameLower ?? column.name.toLowerCase()))
|
|
12132
12432
|
);
|
|
12133
|
-
const unconstrained = table.isUnconstrained() && !stmt.upsert && stmt.mode === "insert" && stmt.returning.length === 0 && rowidIndexes.length === 0;
|
|
12433
|
+
const unconstrained = table.isUnconstrained() && env.state.databaseForTable(table).triggers.size === 0 && !stmt.upsert && stmt.mode === "insert" && stmt.returning.length === 0 && rowidIndexes.length === 0;
|
|
12134
12434
|
if (unconstrained) {
|
|
12135
12435
|
for (const source of sourceRows) {
|
|
12136
12436
|
if (source.length !== columnNames.length)
|
|
12137
|
-
throw new SqliteError(
|
|
12437
|
+
throw new SqliteError(
|
|
12438
|
+
stmt.columns ? `${source.length} values for ${columnNames.length} columns` : `table ${table.name} has ${columnNames.length} columns but ${source.length} values were supplied`,
|
|
12439
|
+
"other"
|
|
12440
|
+
);
|
|
12138
12441
|
const values = /* @__PURE__ */ new Map();
|
|
12139
12442
|
for (let index = 0; index < table.columns.length; index++) {
|
|
12140
12443
|
const column = table.columns[index];
|
|
@@ -12150,7 +12453,10 @@ function executeInsert(stmt, env) {
|
|
|
12150
12453
|
}
|
|
12151
12454
|
for (const source of sourceRows) {
|
|
12152
12455
|
if (source.length !== columnNames.length)
|
|
12153
|
-
throw new SqliteError(
|
|
12456
|
+
throw new SqliteError(
|
|
12457
|
+
stmt.columns ? `${source.length} values for ${columnNames.length} columns` : `table ${table.name} has ${columnNames.length} columns but ${source.length} values were supplied`,
|
|
12458
|
+
"other"
|
|
12459
|
+
);
|
|
12154
12460
|
const values = /* @__PURE__ */ new Map();
|
|
12155
12461
|
for (const column of table.columns) {
|
|
12156
12462
|
if (column.generated) continue;
|
|
@@ -12238,9 +12544,12 @@ function executeInsert(stmt, env) {
|
|
|
12238
12544
|
validateRow(table, row, env);
|
|
12239
12545
|
if (table.indexes.length > 0) addIndexes(table, row, env);
|
|
12240
12546
|
if (env.state.foreignKeysEnabled) checkForeignKeys(table, row, env);
|
|
12547
|
+
if (!table.withoutRowid) {
|
|
12548
|
+
last = rowid;
|
|
12549
|
+
env.state.lastInsertRowid = rowid;
|
|
12550
|
+
}
|
|
12241
12551
|
fireInsertTriggers("AFTER", table, row.values, null, env);
|
|
12242
12552
|
changes++;
|
|
12243
|
-
last = table.withoutRowid ? 0 : rowid;
|
|
12244
12553
|
if (stmt.returning.length)
|
|
12245
12554
|
returningRows.push(projectReturning(stmt.returning, scopeFor(table, row, stmt.table, env), env));
|
|
12246
12555
|
} catch (error) {
|
|
@@ -12253,12 +12562,12 @@ function executeInsert(stmt, env) {
|
|
|
12253
12562
|
throw error;
|
|
12254
12563
|
}
|
|
12255
12564
|
}
|
|
12256
|
-
|
|
12257
|
-
if (stmt.returning.length === 0) return emptyResult(
|
|
12258
|
-
return valuesToResult(returningNames(stmt.returning, table), returningRows,
|
|
12565
|
+
const reportedChanges = finalizeDmlChanges(totalBefore, changes, env, last);
|
|
12566
|
+
if (stmt.returning.length === 0) return emptyResult(reportedChanges, last);
|
|
12567
|
+
return valuesToResult(returningNames(stmt.returning, table), returningRows, reportedChanges, last);
|
|
12259
12568
|
}
|
|
12260
12569
|
function evaluateInsertSource(stmt, env) {
|
|
12261
|
-
if (stmt.select) return resultValues(executeSelect2(
|
|
12570
|
+
if (stmt.select) return resultValues(executeSelect2(stmt.select, env));
|
|
12262
12571
|
if (!stmt.values) return [[]];
|
|
12263
12572
|
const ctx = env.createEvalContext();
|
|
12264
12573
|
return stmt.values.map(
|
|
@@ -12287,6 +12596,7 @@ function tryFastInsert(stmt, env) {
|
|
|
12287
12596
|
} catch {
|
|
12288
12597
|
return null;
|
|
12289
12598
|
}
|
|
12599
|
+
if (env.state.databaseForTable(table).triggers.size > 0) return null;
|
|
12290
12600
|
if (!table.isUnconstrained()) return null;
|
|
12291
12601
|
const values = /* @__PURE__ */ new Map();
|
|
12292
12602
|
for (const slot of plan.slots) values.set(slot.key, applyAffinity(slot.read(env), slot.affinity));
|
|
@@ -12331,9 +12641,20 @@ function buildFastInsertPlan(stmt, env) {
|
|
|
12331
12641
|
return { tableName: stmt.table, slots };
|
|
12332
12642
|
}
|
|
12333
12643
|
function executeUpdate(stmt, env) {
|
|
12644
|
+
try {
|
|
12645
|
+
return withDmlCtes(stmt.with, env, () => executeUpdateCore(stmt, env));
|
|
12646
|
+
} catch (error) {
|
|
12647
|
+
handleConflictRollback(stmt.or === "rollback", error, env);
|
|
12648
|
+
throw error;
|
|
12649
|
+
}
|
|
12650
|
+
}
|
|
12651
|
+
function executeUpdateCore(stmt, env) {
|
|
12334
12652
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12335
12653
|
return executeVirtualUpdate(stmt, env);
|
|
12336
12654
|
}
|
|
12655
|
+
const totalBefore = env.state.totalChanges;
|
|
12656
|
+
const view = writableView(stmt.table, "UPDATE", env);
|
|
12657
|
+
if (view) return executeViewUpdate(stmt, view, env, totalBefore);
|
|
12337
12658
|
const table = env.state.getWritableTable(stmt.table);
|
|
12338
12659
|
const alias = stmt.alias ?? stmt.table;
|
|
12339
12660
|
const candidates = [];
|
|
@@ -12386,13 +12707,24 @@ function executeUpdate(stmt, env) {
|
|
|
12386
12707
|
throw error;
|
|
12387
12708
|
}
|
|
12388
12709
|
}
|
|
12389
|
-
|
|
12390
|
-
return valuesToResult(
|
|
12710
|
+
const reportedChanges = finalizeDmlChanges(totalBefore, changes, env);
|
|
12711
|
+
return valuesToResult(
|
|
12712
|
+
returningNames(stmt.returning, table),
|
|
12713
|
+
returningRows,
|
|
12714
|
+
reportedChanges,
|
|
12715
|
+
env.state.lastInsertRowid
|
|
12716
|
+
);
|
|
12391
12717
|
}
|
|
12392
12718
|
function executeDelete(stmt, env) {
|
|
12719
|
+
return withDmlCtes(stmt.with, env, () => executeDeleteCore(stmt, env));
|
|
12720
|
+
}
|
|
12721
|
+
function executeDeleteCore(stmt, env) {
|
|
12393
12722
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12394
12723
|
return executeVirtualDelete(stmt, env);
|
|
12395
12724
|
}
|
|
12725
|
+
const totalBefore = env.state.totalChanges;
|
|
12726
|
+
const view = writableView(stmt.table, "DELETE", env);
|
|
12727
|
+
if (view) return executeViewDelete(stmt, view, env, totalBefore);
|
|
12396
12728
|
const table = env.state.getWritableTable(stmt.table);
|
|
12397
12729
|
const selectedSource = stmt.where === null ? null : tryIndexedTableRows({ type: "table", schema: null, name: stmt.table, alias: stmt.alias }, stmt.where, env);
|
|
12398
12730
|
const selected = [...selectedSource ? selectedSource.rows : table.scan()].filter((row) => {
|
|
@@ -12410,8 +12742,120 @@ function executeDelete(stmt, env) {
|
|
|
12410
12742
|
fireDeleteTriggers("AFTER", table, row, env);
|
|
12411
12743
|
changes++;
|
|
12412
12744
|
}
|
|
12413
|
-
|
|
12414
|
-
return valuesToResult(
|
|
12745
|
+
const reportedChanges = finalizeDmlChanges(totalBefore, changes, env);
|
|
12746
|
+
return valuesToResult(
|
|
12747
|
+
returningNames(stmt.returning, table),
|
|
12748
|
+
returningRows,
|
|
12749
|
+
reportedChanges,
|
|
12750
|
+
env.state.lastInsertRowid
|
|
12751
|
+
);
|
|
12752
|
+
}
|
|
12753
|
+
function withDmlCtes(withClause, env, execute) {
|
|
12754
|
+
if (!withClause) return execute();
|
|
12755
|
+
const savedCtes = new Map(env.ctes);
|
|
12756
|
+
try {
|
|
12757
|
+
executeWith(withClause, env);
|
|
12758
|
+
return execute();
|
|
12759
|
+
} finally {
|
|
12760
|
+
env.ctes.clear();
|
|
12761
|
+
for (const [name, result] of savedCtes) env.ctes.set(name, result);
|
|
12762
|
+
}
|
|
12763
|
+
}
|
|
12764
|
+
function handleConflictRollback(rollback, error, env) {
|
|
12765
|
+
if (rollback && env.transactions.inTransaction && error instanceof SqliteError && error.category.startsWith("constraint")) {
|
|
12766
|
+
env.transactions.rollback();
|
|
12767
|
+
}
|
|
12768
|
+
}
|
|
12769
|
+
function writableView(name, event, env) {
|
|
12770
|
+
const { schema, bare } = splitQualifiedName(name);
|
|
12771
|
+
const db = env.state.databaseForSchema(schema, name);
|
|
12772
|
+
const view = db.views.get(bare.toLowerCase());
|
|
12773
|
+
if (!view) return null;
|
|
12774
|
+
const hasInsteadOf = [...db.triggers.values()].some(
|
|
12775
|
+
(trigger) => trigger.tableName.toLowerCase() === bare.toLowerCase() && trigger.event === event && trigger.timing === "INSTEAD"
|
|
12776
|
+
);
|
|
12777
|
+
if (!hasInsteadOf) {
|
|
12778
|
+
env.state.getWritableTable(name);
|
|
12779
|
+
throw new SqliteError(`cannot modify ${bare} because it is a view`, "other");
|
|
12780
|
+
}
|
|
12781
|
+
const names = view.columns ?? executeSelect2(view.select, env).columns;
|
|
12782
|
+
return {
|
|
12783
|
+
schema,
|
|
12784
|
+
name: bare,
|
|
12785
|
+
view,
|
|
12786
|
+
table: new Table(
|
|
12787
|
+
bare,
|
|
12788
|
+
names.map((column) => makeColumnInfo(column, null))
|
|
12789
|
+
)
|
|
12790
|
+
};
|
|
12791
|
+
}
|
|
12792
|
+
function executeViewInsert(stmt, target, env, totalBefore) {
|
|
12793
|
+
const columnNames = stmt.columns ?? target.table.columns.map((column) => column.name);
|
|
12794
|
+
for (const name of columnNames) columnOf(target.table, name);
|
|
12795
|
+
const suppliedIndexes = target.table.columns.map(
|
|
12796
|
+
(column) => columnNames.findIndex((name) => name.toLowerCase() === normalizeColumnName(column.name))
|
|
12797
|
+
);
|
|
12798
|
+
for (const source of evaluateInsertSource(stmt, env)) {
|
|
12799
|
+
if (source.length !== columnNames.length) {
|
|
12800
|
+
throw new SqliteError(`${source.length} values for ${columnNames.length} columns`, "other");
|
|
12801
|
+
}
|
|
12802
|
+
const values = /* @__PURE__ */ new Map();
|
|
12803
|
+
target.table.columns.forEach((column, index) => {
|
|
12804
|
+
const supplied = suppliedIndexes[index] ?? -1;
|
|
12805
|
+
values.set(normalizeColumnName(column.name), supplied < 0 ? null : source[supplied] ?? null);
|
|
12806
|
+
});
|
|
12807
|
+
fireInsertTriggers("INSTEAD", target.table, values, null, env);
|
|
12808
|
+
}
|
|
12809
|
+
const changes = finalizeDmlChanges(totalBefore, 0, env);
|
|
12810
|
+
return emptyResult(changes, env.state.lastInsertRowid);
|
|
12811
|
+
}
|
|
12812
|
+
function executeViewUpdate(stmt, target, env, totalBefore) {
|
|
12813
|
+
const alias = stmt.alias ?? target.name;
|
|
12814
|
+
const scopes = scanView(target, alias, env);
|
|
12815
|
+
const updatedColumns = new Set(
|
|
12816
|
+
stmt.set.flatMap((item) => item.columns.map((name) => columnOf(target.table, name).name))
|
|
12817
|
+
);
|
|
12818
|
+
for (const scope of scopes) {
|
|
12819
|
+
const ctx = env.createEvalContext(scope);
|
|
12820
|
+
if (stmt.where && isTruthySql(evalExpr(stmt.where, ctx)) !== true) continue;
|
|
12821
|
+
const oldRow = viewRow(target.table, scope);
|
|
12822
|
+
const updates = evaluateSet(stmt.set, target.table, ctx);
|
|
12823
|
+
const newValues = mergedValues(target.table, oldRow, updates);
|
|
12824
|
+
fireUpdateTriggers("INSTEAD", target.table, oldRow, newValues, updatedColumns, env);
|
|
12825
|
+
}
|
|
12826
|
+
const changes = finalizeDmlChanges(totalBefore, 0, env);
|
|
12827
|
+
return emptyResult(changes, env.state.lastInsertRowid);
|
|
12828
|
+
}
|
|
12829
|
+
function executeViewDelete(stmt, target, env, totalBefore) {
|
|
12830
|
+
const alias = stmt.alias ?? target.name;
|
|
12831
|
+
for (const scope of scanView(target, alias, env)) {
|
|
12832
|
+
if (stmt.where && isTruthySql(evalExpr(stmt.where, env.createEvalContext(scope))) !== true) continue;
|
|
12833
|
+
fireDeleteTriggers("INSTEAD", target.table, viewRow(target.table, scope), env);
|
|
12834
|
+
}
|
|
12835
|
+
const changes = finalizeDmlChanges(totalBefore, 0, env);
|
|
12836
|
+
return emptyResult(changes, env.state.lastInsertRowid);
|
|
12837
|
+
}
|
|
12838
|
+
function scanView(target, alias, env) {
|
|
12839
|
+
return scanFrom(
|
|
12840
|
+
{ type: "table", schema: target.schema, name: target.name, alias: alias === target.name ? null : alias },
|
|
12841
|
+
env
|
|
12842
|
+
);
|
|
12843
|
+
}
|
|
12844
|
+
function viewRow(table, scope) {
|
|
12845
|
+
const values = /* @__PURE__ */ new Map();
|
|
12846
|
+
for (const column of table.columns) {
|
|
12847
|
+
const key = normalizeColumnName(column.name);
|
|
12848
|
+
const cell = scope.cells.find((candidate) => normalizeColumnName(candidate.name) === key);
|
|
12849
|
+
values.set(key, cell?.value ?? null);
|
|
12850
|
+
}
|
|
12851
|
+
return { rowid: scope.rowid ?? 0, values };
|
|
12852
|
+
}
|
|
12853
|
+
function finalizeDmlChanges(totalBefore, directChanges, env, last) {
|
|
12854
|
+
const triggerChanges = env.state.totalChanges - totalBefore;
|
|
12855
|
+
env.state.recordChange(directChanges, last);
|
|
12856
|
+
const reportedChanges = triggerChanges + directChanges;
|
|
12857
|
+
env.state.changes = reportedChanges;
|
|
12858
|
+
return reportedChanges;
|
|
12415
12859
|
}
|
|
12416
12860
|
function mergedValues(table, row, updates) {
|
|
12417
12861
|
const values = /* @__PURE__ */ new Map();
|
|
@@ -12722,7 +13166,7 @@ function applyReferentialDelete(parent, row, env) {
|
|
|
12722
13166
|
} else if (constraint.onDelete === "SET DEFAULT") {
|
|
12723
13167
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
12724
13168
|
changes += 1 + updated.cascaded;
|
|
12725
|
-
} else if (!fkIsDeferred(constraint, env)) {
|
|
13169
|
+
} else if (constraint.onDelete === "RESTRICT" || !fkIsDeferred(constraint, env)) {
|
|
12726
13170
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12727
13171
|
}
|
|
12728
13172
|
}
|
|
@@ -12765,7 +13209,7 @@ function applyReferentialUpdate(parent, before, after, env) {
|
|
|
12765
13209
|
} else if (constraint.onUpdate === "SET DEFAULT") {
|
|
12766
13210
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
12767
13211
|
changes += 1 + updated.cascaded;
|
|
12768
|
-
} else if (!fkIsDeferred(constraint, env)) {
|
|
13212
|
+
} else if (constraint.onUpdate === "RESTRICT" || !fkIsDeferred(constraint, env)) {
|
|
12769
13213
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12770
13214
|
}
|
|
12771
13215
|
}
|
|
@@ -13046,6 +13490,11 @@ function executePragma(name, expr, env) {
|
|
|
13046
13490
|
if (!env.transactions.inTransaction) env.state.foreignKeysEnabled = coercePragmaTruthy(value);
|
|
13047
13491
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
13048
13492
|
}
|
|
13493
|
+
if (key === "case_sensitive_like" && expr !== null) {
|
|
13494
|
+
const value = evalPragmaSetValue(expr, env);
|
|
13495
|
+
env.state.caseSensitiveLike = coercePragmaTruthy(value);
|
|
13496
|
+
return emptyResult(0, env.state.lastInsertRowid);
|
|
13497
|
+
}
|
|
13049
13498
|
if ((key === "user_version" || key === "schema_version") && expr !== null) {
|
|
13050
13499
|
const value = evalPragmaSetValue(expr, env);
|
|
13051
13500
|
const num2 = coercePragmaInt(value);
|
|
@@ -13258,7 +13707,10 @@ var Statement = class _Statement {
|
|
|
13258
13707
|
execute(params, options) {
|
|
13259
13708
|
this.database.assertOpen();
|
|
13260
13709
|
this.reprepareIfSchemaChanged();
|
|
13261
|
-
if (this.statements.length === 0)
|
|
13710
|
+
if (this.statements.length === 0) {
|
|
13711
|
+
if (options?.named) throw new SqliteError("empty statement", "misuse");
|
|
13712
|
+
return emptyResult(this.database.state.changes, this.database.state.lastInsertRowid);
|
|
13713
|
+
}
|
|
13262
13714
|
this.namedPlan ??= planNamedParameters(this.sql);
|
|
13263
13715
|
const expected = this.namedPlan.expectedCount;
|
|
13264
13716
|
if (params.length > 0 && params.length !== expected) {
|
|
@@ -13330,11 +13782,18 @@ function planNamedParameters(sql) {
|
|
|
13330
13782
|
var Database = class {
|
|
13331
13783
|
/** @internal Engine catalog, tables, and mutation counters. */
|
|
13332
13784
|
state = new DatabaseState();
|
|
13333
|
-
/** Seed used to construct the PRNG. */
|
|
13785
|
+
/** Seed used to construct the PRNG. Ignored when {@link randomMode} is `"os"`. */
|
|
13334
13786
|
seed;
|
|
13787
|
+
/** Entropy mode for `random()` / `randomblob()`. */
|
|
13788
|
+
randomMode;
|
|
13789
|
+
/**
|
|
13790
|
+
* When true, `'now'` follows the wall clock and {@link restore} does not freeze it.
|
|
13791
|
+
* @internal
|
|
13792
|
+
*/
|
|
13793
|
+
systemClock;
|
|
13335
13794
|
/**
|
|
13336
13795
|
* PRNG backing `random()` / `randomblob()` and related builtins.
|
|
13337
|
-
* Prefer passing `seed` to the constructor.
|
|
13796
|
+
* Prefer passing `seed` / `random` to the constructor.
|
|
13338
13797
|
* @internal
|
|
13339
13798
|
*/
|
|
13340
13799
|
prng;
|
|
@@ -13357,7 +13816,9 @@ var Database = class {
|
|
|
13357
13816
|
*/
|
|
13358
13817
|
constructor(options = {}) {
|
|
13359
13818
|
this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
|
|
13360
|
-
this.
|
|
13819
|
+
this.randomMode = options.random ?? "deterministic";
|
|
13820
|
+
this.systemClock = options.now === "system";
|
|
13821
|
+
this.prng = this.randomMode === "os" ? new OsEntropy() : new Prng(this.seed);
|
|
13361
13822
|
this.now = resolveClock(options.now);
|
|
13362
13823
|
this.transactions = new TransactionManager(this.state, this.prng);
|
|
13363
13824
|
}
|
|
@@ -13471,7 +13932,7 @@ var Database = class {
|
|
|
13471
13932
|
this.state.replaceWith(decoded.state, { adopt: true });
|
|
13472
13933
|
if (decoded.runtime) {
|
|
13473
13934
|
this.prng.setState(decoded.runtime.prngState);
|
|
13474
|
-
this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
13935
|
+
if (!this.systemClock) this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
13475
13936
|
}
|
|
13476
13937
|
}
|
|
13477
13938
|
/**
|
|
@@ -13506,6 +13967,15 @@ var Database = class {
|
|
|
13506
13967
|
this.assertOpen();
|
|
13507
13968
|
return this.state.lastInsertRowid;
|
|
13508
13969
|
}
|
|
13970
|
+
/**
|
|
13971
|
+
* Cumulative rows changed by INSERT / UPDATE / DELETE (SQLite `total_changes()`).
|
|
13972
|
+
*
|
|
13973
|
+
* @throws {SqliteError} If the database is closed.
|
|
13974
|
+
*/
|
|
13975
|
+
get totalChanges() {
|
|
13976
|
+
this.assertOpen();
|
|
13977
|
+
return this.state.totalChanges;
|
|
13978
|
+
}
|
|
13509
13979
|
/**
|
|
13510
13980
|
* Throw if {@link close} has already been called.
|
|
13511
13981
|
* @internal
|