@crvouga/sqlite-mem 1.1.1 → 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 +17 -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 +12 -1
- 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 +698 -187
- package/dist/index.js.map +4 -4
- package/dist/lexer/tokenize.d.ts +1 -1
- 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 +266 -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
|
@@ -91,6 +91,7 @@ var KEYWORDS = {
|
|
|
91
91
|
FOREIGN: "FOREIGN",
|
|
92
92
|
FROM: "FROM",
|
|
93
93
|
FULL: "FULL",
|
|
94
|
+
FALSE: "FALSE",
|
|
94
95
|
GENERATED: "GENERATED",
|
|
95
96
|
GLOB: "GLOB",
|
|
96
97
|
GROUP: "GROUP",
|
|
@@ -165,6 +166,7 @@ var KEYWORDS = {
|
|
|
165
166
|
TO: "TO",
|
|
166
167
|
TRANSACTION: "TRANSACTION",
|
|
167
168
|
TRIGGER: "TRIGGER",
|
|
169
|
+
TRUE: "TRUE",
|
|
168
170
|
UNBOUNDED: "UNBOUNDED",
|
|
169
171
|
UNION: "UNION",
|
|
170
172
|
UNIQUE: "UNIQUE",
|
|
@@ -519,6 +521,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
519
521
|
"FOREIGN",
|
|
520
522
|
"FROM",
|
|
521
523
|
"FULL",
|
|
524
|
+
"FALSE",
|
|
522
525
|
"GENERATED",
|
|
523
526
|
"GLOB",
|
|
524
527
|
"GROUP",
|
|
@@ -593,6 +596,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
593
596
|
"TO",
|
|
594
597
|
"TRANSACTION",
|
|
595
598
|
"TRIGGER",
|
|
599
|
+
"TRUE",
|
|
596
600
|
"UNBOUNDED",
|
|
597
601
|
"UNION",
|
|
598
602
|
"UNIQUE",
|
|
@@ -741,13 +745,20 @@ var Parser = class {
|
|
|
741
745
|
const stmt = this.parseStatement();
|
|
742
746
|
return { type: "explain", queryPlan, statement: stmt };
|
|
743
747
|
}
|
|
744
|
-
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
|
+
}
|
|
745
756
|
if (this.at("SELECT")) return this.parseSelectStmt();
|
|
746
|
-
if (this.
|
|
757
|
+
if (this.at("INSERT") || this.at("REPLACE")) {
|
|
747
758
|
return this.parseInsertStmt();
|
|
748
759
|
}
|
|
749
|
-
if (this.
|
|
750
|
-
if (this.
|
|
760
|
+
if (this.at("UPDATE")) return this.parseUpdateStmt();
|
|
761
|
+
if (this.at("DELETE")) return this.parseDeleteStmt();
|
|
751
762
|
if (this.at("CREATE")) return this.parseCreateStmt();
|
|
752
763
|
if (this.at("DROP")) return this.parseDropStmt();
|
|
753
764
|
if (this.at("ALTER")) return this.parseAlterTableStmt();
|
|
@@ -809,12 +820,15 @@ var Parser = class {
|
|
|
809
820
|
this.expect("RPAREN");
|
|
810
821
|
}
|
|
811
822
|
this.expect("AS");
|
|
812
|
-
|
|
813
|
-
|
|
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";
|
|
814
828
|
this.expect("LPAREN");
|
|
815
|
-
const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.parseSelectCore();
|
|
829
|
+
const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.at("WITH") ? this.parseSelectStmt() : this.parseSelectCore();
|
|
816
830
|
this.expect("RPAREN");
|
|
817
|
-
ctes.push({ name, columns, select });
|
|
831
|
+
ctes.push({ name, columns, materialized, select });
|
|
818
832
|
} while (this.match("COMMA"));
|
|
819
833
|
return { recursive, ctes };
|
|
820
834
|
}
|
|
@@ -822,8 +836,7 @@ var Parser = class {
|
|
|
822
836
|
return this.at("WITH") ? this.parseWithClause() : null;
|
|
823
837
|
}
|
|
824
838
|
// ── SELECT ──────────────────────────────────────────────────────────────
|
|
825
|
-
parseSelectStmt() {
|
|
826
|
-
const withClause = this.parseOptionalWith();
|
|
839
|
+
parseSelectStmt(withClause = this.parseOptionalWith()) {
|
|
827
840
|
const select = this.parseSelectCore();
|
|
828
841
|
select.with = withClause;
|
|
829
842
|
return select;
|
|
@@ -1121,8 +1134,7 @@ var Parser = class {
|
|
|
1121
1134
|
return { limit: first, offset };
|
|
1122
1135
|
}
|
|
1123
1136
|
// ── INSERT / REPLACE ────────────────────────────────────────────────────
|
|
1124
|
-
parseInsertStmt() {
|
|
1125
|
-
const withClause = this.parseOptionalWith();
|
|
1137
|
+
parseInsertStmt(withClause = this.parseOptionalWith()) {
|
|
1126
1138
|
let mode = "insert";
|
|
1127
1139
|
if (this.match("REPLACE")) {
|
|
1128
1140
|
mode = "replace";
|
|
@@ -1225,8 +1237,7 @@ var Parser = class {
|
|
|
1225
1237
|
return this.parseResultColumns();
|
|
1226
1238
|
}
|
|
1227
1239
|
// ── UPDATE ──────────────────────────────────────────────────────────────
|
|
1228
|
-
parseUpdateStmt() {
|
|
1229
|
-
const withClause = this.parseOptionalWith();
|
|
1240
|
+
parseUpdateStmt(withClause = this.parseOptionalWith()) {
|
|
1230
1241
|
this.expect("UPDATE");
|
|
1231
1242
|
const or = this.mapUpdateOr(this.parseOrConflict());
|
|
1232
1243
|
const table = this.parseTableName();
|
|
@@ -1241,8 +1252,7 @@ var Parser = class {
|
|
|
1241
1252
|
return { type: "update", with: withClause, or, table, alias, set, from, where, returning };
|
|
1242
1253
|
}
|
|
1243
1254
|
// ── DELETE ──────────────────────────────────────────────────────────────
|
|
1244
|
-
parseDeleteStmt() {
|
|
1245
|
-
const withClause = this.parseOptionalWith();
|
|
1255
|
+
parseDeleteStmt(withClause = this.parseOptionalWith()) {
|
|
1246
1256
|
this.expect("DELETE");
|
|
1247
1257
|
this.expect("FROM");
|
|
1248
1258
|
const table = this.parseTableName();
|
|
@@ -1911,6 +1921,24 @@ var Parser = class {
|
|
|
1911
1921
|
left = this.parseLikeRhs(left, true, "GLOB");
|
|
1912
1922
|
continue;
|
|
1913
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
|
+
}
|
|
1914
1942
|
if (n.kind === "BETWEEN") {
|
|
1915
1943
|
this.advance();
|
|
1916
1944
|
this.advance();
|
|
@@ -1939,6 +1967,14 @@ var Parser = class {
|
|
|
1939
1967
|
continue;
|
|
1940
1968
|
}
|
|
1941
1969
|
const not = this.match("NOT");
|
|
1970
|
+
if (this.match("TRUE")) {
|
|
1971
|
+
left = { type: "is_bool", expr: left, not, sense: true };
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
if (this.match("FALSE")) {
|
|
1975
|
+
left = { type: "is_bool", expr: left, not, sense: false };
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1942
1978
|
const right2 = this.parseIsRhs();
|
|
1943
1979
|
left = { type: "binary", op: not ? "IS NOT" : "IS", left, right: right2 };
|
|
1944
1980
|
continue;
|
|
@@ -1958,6 +1994,12 @@ var Parser = class {
|
|
|
1958
1994
|
left = this.parseLikeRhs(left, false, "GLOB");
|
|
1959
1995
|
continue;
|
|
1960
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
|
+
}
|
|
1961
2003
|
if (this.at("MATCH") && PREC.IS_IN_LIKE >= minPrec) {
|
|
1962
2004
|
this.advance();
|
|
1963
2005
|
const right2 = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
|
|
@@ -2116,7 +2158,7 @@ var Parser = class {
|
|
|
2116
2158
|
}
|
|
2117
2159
|
if (this.at("CURRENT_DATE") || this.at("CURRENT_TIME") || this.at("CURRENT_TIMESTAMP")) {
|
|
2118
2160
|
const tok = this.advance();
|
|
2119
|
-
return { type: "function", name: tok.value, distinct: false, args: [], filter: null };
|
|
2161
|
+
return { type: "function", name: tok.value, distinct: false, args: [], orderBy: [], filter: null };
|
|
2120
2162
|
}
|
|
2121
2163
|
if (this.at("NUMBER")) {
|
|
2122
2164
|
const tok = this.advance();
|
|
@@ -2199,6 +2241,7 @@ var Parser = class {
|
|
|
2199
2241
|
} while (this.match("COMMA"));
|
|
2200
2242
|
}
|
|
2201
2243
|
}
|
|
2244
|
+
const orderBy = this.parseOrderBy();
|
|
2202
2245
|
this.expect("RPAREN");
|
|
2203
2246
|
if (this.match("FILTER")) {
|
|
2204
2247
|
this.expect("LPAREN");
|
|
@@ -2219,13 +2262,13 @@ var Parser = class {
|
|
|
2219
2262
|
window = this.parseWindowSpec();
|
|
2220
2263
|
this.expect("RPAREN");
|
|
2221
2264
|
}
|
|
2222
|
-
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 };
|
|
2223
2266
|
return { type: "window", func, window };
|
|
2224
2267
|
}
|
|
2225
2268
|
if (isAgg) {
|
|
2226
|
-
return { type: "aggregate", name: upper, distinct, args, filter };
|
|
2269
|
+
return { type: "aggregate", name: upper, distinct, args, orderBy, filter };
|
|
2227
2270
|
}
|
|
2228
|
-
return { type: "function", name, distinct, args, filter };
|
|
2271
|
+
return { type: "function", name, distinct, args, orderBy, filter };
|
|
2229
2272
|
}
|
|
2230
2273
|
parseCaseExpr(base) {
|
|
2231
2274
|
const whens = [];
|
|
@@ -2333,8 +2376,12 @@ function fixedClock(instant = DEFAULT_NOW) {
|
|
|
2333
2376
|
if (Number.isNaN(ms)) throw new RangeError("invalid clock instant");
|
|
2334
2377
|
return () => new Date(ms);
|
|
2335
2378
|
}
|
|
2379
|
+
function systemClock() {
|
|
2380
|
+
return () => /* @__PURE__ */ new Date();
|
|
2381
|
+
}
|
|
2336
2382
|
function resolveClock(now) {
|
|
2337
2383
|
if (now === void 0) return fixedClock(DEFAULT_NOW);
|
|
2384
|
+
if (now === "system") return systemClock();
|
|
2338
2385
|
if (typeof now === "function") return () => new Date(now().getTime());
|
|
2339
2386
|
return fixedClock(now);
|
|
2340
2387
|
}
|
|
@@ -2399,6 +2446,28 @@ var Prng = class _Prng {
|
|
|
2399
2446
|
return copy;
|
|
2400
2447
|
}
|
|
2401
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
|
+
};
|
|
2402
2471
|
|
|
2403
2472
|
// src/types/value.ts
|
|
2404
2473
|
var SqlReal = class {
|
|
@@ -2505,6 +2574,23 @@ function applyAffinity(value, affinity) {
|
|
|
2505
2574
|
return value;
|
|
2506
2575
|
}
|
|
2507
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
|
+
}
|
|
2508
2594
|
function coerceToNumber(value) {
|
|
2509
2595
|
if (value === null) return null;
|
|
2510
2596
|
if (value instanceof SqlReal) return value.value;
|
|
@@ -2661,14 +2747,18 @@ function jsonErrorPosition(input) {
|
|
|
2661
2747
|
}
|
|
2662
2748
|
}
|
|
2663
2749
|
function isValidJsonText(input, flags = 0) {
|
|
2664
|
-
const
|
|
2750
|
+
const allowCanonical = flags === 0 || (flags & 1) !== 0;
|
|
2751
|
+
const allowJson5 = (flags & 2) !== 0;
|
|
2665
2752
|
try {
|
|
2666
2753
|
if (allowJson5) {
|
|
2667
2754
|
parseJsonText(input);
|
|
2668
2755
|
return true;
|
|
2669
2756
|
}
|
|
2670
|
-
|
|
2671
|
-
|
|
2757
|
+
if (allowCanonical) {
|
|
2758
|
+
parseJsonText(input, { strictCanonical: true });
|
|
2759
|
+
return true;
|
|
2760
|
+
}
|
|
2761
|
+
return false;
|
|
2672
2762
|
} catch {
|
|
2673
2763
|
return false;
|
|
2674
2764
|
}
|
|
@@ -4197,7 +4287,11 @@ function applyModifier(date, modifier) {
|
|
|
4197
4287
|
const normalized = modifier.trim().toLowerCase();
|
|
4198
4288
|
if (normalized === "unixepoch") return new Date((date.getTime() / 864e5 + JULIAN_UNIX_EPOCH) * 1e3);
|
|
4199
4289
|
if (normalized === "utc" || normalized === "localtime") return result;
|
|
4200
|
-
|
|
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);
|
|
4201
4295
|
else if (normalized === "start of month") {
|
|
4202
4296
|
result.setUTCDate(1);
|
|
4203
4297
|
result.setUTCHours(0, 0, 0, 0);
|
|
@@ -4655,13 +4749,15 @@ var mathFunctions = {
|
|
|
4655
4749
|
function escapeRegexChar(char) {
|
|
4656
4750
|
return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
|
|
4657
4751
|
}
|
|
4658
|
-
function likeLiteral(char) {
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
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
|
+
}
|
|
4662
4758
|
return escapeRegexChar(char);
|
|
4663
4759
|
}
|
|
4664
|
-
function likeMatch(text2, pattern, escape = null) {
|
|
4760
|
+
function likeMatch(text2, pattern, escape = null, caseSensitive = false) {
|
|
4665
4761
|
if (escape !== null && [...escape].length !== 1) {
|
|
4666
4762
|
throw new SqliteError("ESCAPE expression must be a single character", "other");
|
|
4667
4763
|
}
|
|
@@ -4671,13 +4767,13 @@ function likeMatch(text2, pattern, escape = null) {
|
|
|
4671
4767
|
const char = chars[i];
|
|
4672
4768
|
if (escape !== null && char === escape) {
|
|
4673
4769
|
const next = chars[++i];
|
|
4674
|
-
source += next === void 0 ? likeLiteral(char) : likeLiteral(next);
|
|
4770
|
+
source += next === void 0 ? likeLiteral(char, caseSensitive) : likeLiteral(next, caseSensitive);
|
|
4675
4771
|
} else if (char === "%") {
|
|
4676
4772
|
source += "[\\s\\S]*";
|
|
4677
4773
|
} else if (char === "_") {
|
|
4678
4774
|
source += "[\\s\\S]";
|
|
4679
4775
|
} else {
|
|
4680
|
-
source += likeLiteral(char);
|
|
4776
|
+
source += likeLiteral(char, caseSensitive);
|
|
4681
4777
|
}
|
|
4682
4778
|
}
|
|
4683
4779
|
return new RegExp(`${source}$`).test(text2);
|
|
@@ -4984,11 +5080,11 @@ var scalarFunctions = {
|
|
|
4984
5080
|
requireArgs3("load_extension", args, 1, 2);
|
|
4985
5081
|
throw new SqliteError("not authorized", "misuse");
|
|
4986
5082
|
},
|
|
4987
|
-
like(args) {
|
|
5083
|
+
like(args, context) {
|
|
4988
5084
|
requireArgs3("like", args, 2, 3);
|
|
4989
5085
|
if (args[0] === null || args[1] === null || args[2] === null) return null;
|
|
4990
5086
|
const escape = args[2] === void 0 ? null : text(args[2]);
|
|
4991
|
-
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;
|
|
4992
5088
|
},
|
|
4993
5089
|
glob(args) {
|
|
4994
5090
|
requireArgs3("glob", args, 2);
|
|
@@ -5333,7 +5429,8 @@ function sqlOr(left, right) {
|
|
|
5333
5429
|
if (leftTruth === null || rightTruth === null) return null;
|
|
5334
5430
|
return 0;
|
|
5335
5431
|
}
|
|
5336
|
-
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);
|
|
5337
5434
|
if (op === "IS" || op === "IS NOT" || op === "IS DISTINCT FROM" || op === "IS NOT DISTINCT FROM") {
|
|
5338
5435
|
const equal = left === null || right === null ? left === right : (collation ? compareWithCollation(left, right, collation) : compareSql(left, right)) === 0;
|
|
5339
5436
|
if (op === "IS" || op === "IS NOT DISTINCT FROM") return booleanValue(equal);
|
|
@@ -5386,11 +5483,18 @@ function evalBinary(op, leftExpr, rightExpr, ctx) {
|
|
|
5386
5483
|
if (["=", "==", "!=", "<>", "<", "<=", ">", ">=", "IS", "IS NOT", "IS DISTINCT FROM", "IS NOT DISTINCT FROM"].includes(
|
|
5387
5484
|
op
|
|
5388
5485
|
)) {
|
|
5389
|
-
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
|
+
);
|
|
5390
5494
|
}
|
|
5391
5495
|
if (op === "LIKE" || op === "NOT LIKE" || op === "GLOB" || op === "NOT GLOB") {
|
|
5392
5496
|
if (left === null || right === null) return null;
|
|
5393
|
-
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));
|
|
5394
5498
|
return booleanValue(op.startsWith("NOT") ? !matches : matches);
|
|
5395
5499
|
}
|
|
5396
5500
|
if (left === null || right === null) return null;
|
|
@@ -5527,6 +5631,8 @@ function explicitCollation(expr) {
|
|
|
5527
5631
|
case "unary":
|
|
5528
5632
|
case "cast":
|
|
5529
5633
|
return explicitCollation(expr.expr);
|
|
5634
|
+
case "is_bool":
|
|
5635
|
+
return explicitCollation(expr.expr);
|
|
5530
5636
|
case "binary":
|
|
5531
5637
|
return explicitCollation(expr.left) ?? explicitCollation(expr.right);
|
|
5532
5638
|
case "between":
|
|
@@ -5557,18 +5663,32 @@ function inheritedCollation(expr, ctx) {
|
|
|
5557
5663
|
case "unary":
|
|
5558
5664
|
case "cast":
|
|
5559
5665
|
return inheritedCollation(expr.expr, ctx);
|
|
5666
|
+
case "is_bool":
|
|
5667
|
+
return inheritedCollation(expr.expr, ctx);
|
|
5668
|
+
default:
|
|
5669
|
+
return null;
|
|
5670
|
+
}
|
|
5671
|
+
}
|
|
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);
|
|
5560
5680
|
default:
|
|
5561
5681
|
return null;
|
|
5562
5682
|
}
|
|
5563
5683
|
}
|
|
5564
|
-
function evalIn(left, values, not) {
|
|
5684
|
+
function evalIn(left, values, not, leftAffinity) {
|
|
5565
5685
|
if (values.length === 0) return booleanValue(not);
|
|
5566
5686
|
if (left === null) return null;
|
|
5567
5687
|
let sawNull = false;
|
|
5568
5688
|
for (const value of values) {
|
|
5569
5689
|
if (value === null) {
|
|
5570
5690
|
sawNull = true;
|
|
5571
|
-
} else if (compareSql(left, value) === 0) {
|
|
5691
|
+
} else if (compareSql(...applyComparisonAffinity(left, value, leftAffinity, null)) === 0) {
|
|
5572
5692
|
return booleanValue(!not);
|
|
5573
5693
|
}
|
|
5574
5694
|
}
|
|
@@ -5602,13 +5722,21 @@ function evalExpr(expr, ctx) {
|
|
|
5602
5722
|
if (expr.op === "-") return asNumber(-numberValue(value));
|
|
5603
5723
|
return ~integerValue(value);
|
|
5604
5724
|
}
|
|
5725
|
+
case "is_bool": {
|
|
5726
|
+
const truth = isTruthySql(evalExpr(expr.expr, ctx));
|
|
5727
|
+
if (!expr.not && expr.sense) return booleanValue(truth === true);
|
|
5728
|
+
if (!expr.not && !expr.sense) return booleanValue(truth === false);
|
|
5729
|
+
if (expr.not && expr.sense) return booleanValue(truth !== true);
|
|
5730
|
+
return booleanValue(truth !== false);
|
|
5731
|
+
}
|
|
5605
5732
|
case "binary":
|
|
5606
5733
|
return evalBinary(expr.op, expr.left, expr.right, ctx);
|
|
5607
5734
|
case "between": {
|
|
5608
5735
|
const value = evalExpr(expr.expr, ctx);
|
|
5609
5736
|
const collation = resolveComparisonCollation(expr.expr, expr.lower, ctx) ?? resolveComparisonCollation(expr.expr, expr.upper, ctx) ?? void 0;
|
|
5610
|
-
const
|
|
5611
|
-
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));
|
|
5612
5740
|
if (result === null) return null;
|
|
5613
5741
|
return expr.not ? booleanValue(result === 0) : result;
|
|
5614
5742
|
}
|
|
@@ -5635,14 +5763,19 @@ function evalExpr(expr, ctx) {
|
|
|
5635
5763
|
}
|
|
5636
5764
|
const left = evalExpr(expr.expr, ctx);
|
|
5637
5765
|
const values = Array.isArray(expr.values) ? expr.values.map((value) => evalExpr(value, ctx)) : executeSelect(ctx, expr.values).rows.map((row) => row[0] ?? null);
|
|
5638
|
-
return evalIn(left, values, expr.not);
|
|
5766
|
+
return evalIn(left, values, expr.not, resolveComparisonAffinity(expr.expr, ctx));
|
|
5639
5767
|
}
|
|
5640
5768
|
case "like": {
|
|
5641
5769
|
const value = evalExpr(expr.expr, ctx);
|
|
5642
5770
|
const pattern = evalExpr(expr.pattern, ctx);
|
|
5643
5771
|
const escape = expr.escape === null ? null : evalExpr(expr.escape, ctx);
|
|
5644
5772
|
if (value === null || pattern === null || escape === null && expr.escape !== null) return null;
|
|
5645
|
-
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));
|
|
5646
5779
|
return booleanValue(expr.not ? !match : match);
|
|
5647
5780
|
}
|
|
5648
5781
|
case "case": {
|
|
@@ -6783,7 +6916,7 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
6783
6916
|
insert(values, rowid) {
|
|
6784
6917
|
const command = this.detectCommand(values);
|
|
6785
6918
|
if (command !== null) {
|
|
6786
|
-
this.runCommand(command, values);
|
|
6919
|
+
this.runCommand(command, values, rowid);
|
|
6787
6920
|
return 0;
|
|
6788
6921
|
}
|
|
6789
6922
|
const assigned = rowid ?? this.nextRowid++;
|
|
@@ -6932,18 +7065,63 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
6932
7065
|
}
|
|
6933
7066
|
return parts.join(" ");
|
|
6934
7067
|
}
|
|
6935
|
-
/** FTS3 matchinfo
|
|
6936
|
-
matchinfo(cursor,
|
|
7068
|
+
/** FTS3 matchinfo format encoded as native-style 32-bit little-endian integers. */
|
|
7069
|
+
matchinfo(cursor, format = "pcx") {
|
|
6937
7070
|
const row = this.rows.get(cursor.rowid);
|
|
6938
7071
|
const nPhrase = Math.max(1, cursor.phraseTerms.length);
|
|
6939
7072
|
const nCol = this.columns.length;
|
|
6940
|
-
const values = [
|
|
6941
|
-
for (
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
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");
|
|
6947
7125
|
}
|
|
6948
7126
|
}
|
|
6949
7127
|
const buf = new Uint8Array(values.length * 4);
|
|
@@ -6991,13 +7169,9 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
6991
7169
|
if (!values.has(key)) return null;
|
|
6992
7170
|
const v = values.get(key);
|
|
6993
7171
|
if (typeof v !== "string") return null;
|
|
6994
|
-
const otherContent = [...values.entries()].some(
|
|
6995
|
-
([k, val]) => k !== key && val !== null && this.columns.some((c) => c.toLowerCase() === k)
|
|
6996
|
-
);
|
|
6997
|
-
if (otherContent) return null;
|
|
6998
7172
|
return v;
|
|
6999
7173
|
}
|
|
7000
|
-
runCommand(command, _values) {
|
|
7174
|
+
runCommand(command, _values, rowid) {
|
|
7001
7175
|
const cmd = command.toLowerCase();
|
|
7002
7176
|
if (cmd === "optimize") return;
|
|
7003
7177
|
if (cmd === "rebuild") {
|
|
@@ -7012,13 +7186,15 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7012
7186
|
"other"
|
|
7013
7187
|
);
|
|
7014
7188
|
}
|
|
7015
|
-
for (const
|
|
7189
|
+
for (const rowid2 of [...this.rows.keys()]) this.delete(rowid2);
|
|
7016
7190
|
return;
|
|
7017
7191
|
}
|
|
7018
7192
|
if (cmd.startsWith("merge=") || cmd.startsWith("automerge=")) {
|
|
7019
7193
|
throw new SqliteError("SQL logic error", "other");
|
|
7020
7194
|
}
|
|
7021
7195
|
if (cmd === "delete") {
|
|
7196
|
+
if (rowid === void 0) throw new SqliteError("SQL logic error", "other");
|
|
7197
|
+
this.delete(rowid);
|
|
7022
7198
|
return;
|
|
7023
7199
|
}
|
|
7024
7200
|
throw new SqliteError("SQL logic error", "other");
|
|
@@ -7251,11 +7427,6 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7251
7427
|
}
|
|
7252
7428
|
return n;
|
|
7253
7429
|
}
|
|
7254
|
-
termFreq(row, column, term) {
|
|
7255
|
-
const needle = this.normalizeQueryTerm(term);
|
|
7256
|
-
const tokens = row.tokensByColumn.get(column.toLowerCase()) ?? [];
|
|
7257
|
-
return tokens.filter((t) => t.term === needle || t.term.startsWith(needle)).length;
|
|
7258
|
-
}
|
|
7259
7430
|
docLength(row) {
|
|
7260
7431
|
let n = 0;
|
|
7261
7432
|
for (const col of this.indexedColumns()) n += (row.tokensByColumn.get(col.toLowerCase()) ?? []).length;
|
|
@@ -7519,6 +7690,8 @@ var Table = class _Table {
|
|
|
7519
7690
|
scanCache = null;
|
|
7520
7691
|
/** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
|
|
7521
7692
|
equalityHashes = null;
|
|
7693
|
+
/** Cached maximum rowid. `undefined` means recompute after deleting the maximum. */
|
|
7694
|
+
maximumRowid = null;
|
|
7522
7695
|
frozen = false;
|
|
7523
7696
|
constructor(name, columns, options = {}) {
|
|
7524
7697
|
this.name = name;
|
|
@@ -7721,7 +7894,12 @@ var Table = class _Table {
|
|
|
7721
7894
|
if (alias) values.set(normalizeColumnName(alias.name), targetKey);
|
|
7722
7895
|
const candidate = { rowid: targetKey, values };
|
|
7723
7896
|
this.validate(candidate, key);
|
|
7724
|
-
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
|
+
}
|
|
7725
7903
|
this.rows.set(targetKey, candidate);
|
|
7726
7904
|
this.advanceNextRowid(targetKey);
|
|
7727
7905
|
this.reindexEquality(existing, candidate);
|
|
@@ -7735,6 +7913,9 @@ var Table = class _Table {
|
|
|
7735
7913
|
if (this.withoutRowid) this.clusteredRows.delete(this.makeClusterKey(existing.values));
|
|
7736
7914
|
this.unindexEquality(existing);
|
|
7737
7915
|
this.invalidateScan();
|
|
7916
|
+
if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
|
|
7917
|
+
this.maximumRowid = void 0;
|
|
7918
|
+
}
|
|
7738
7919
|
return this.rows.delete(key);
|
|
7739
7920
|
}
|
|
7740
7921
|
*scan() {
|
|
@@ -7756,6 +7937,7 @@ var Table = class _Table {
|
|
|
7756
7937
|
strict: this.strict
|
|
7757
7938
|
});
|
|
7758
7939
|
copy.nextRowid = this.nextRowid;
|
|
7940
|
+
copy.maximumRowid = this.maximumRowid;
|
|
7759
7941
|
for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
|
|
7760
7942
|
for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
|
|
7761
7943
|
return copy;
|
|
@@ -7768,6 +7950,7 @@ var Table = class _Table {
|
|
|
7768
7950
|
}
|
|
7769
7951
|
/** Rebuild clustered storage after snapshot decode or bulk load. */
|
|
7770
7952
|
rebuildClusteredRows() {
|
|
7953
|
+
this.maximumRowid = void 0;
|
|
7771
7954
|
if (!this.withoutRowid) return;
|
|
7772
7955
|
this.clusteredRows.clear();
|
|
7773
7956
|
for (const row of this.rows.values()) {
|
|
@@ -7876,11 +8059,23 @@ var Table = class _Table {
|
|
|
7876
8059
|
return primary[0];
|
|
7877
8060
|
}
|
|
7878
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
|
+
}
|
|
7879
8071
|
let candidate = canonicalRowid(this.nextRowid);
|
|
7880
8072
|
while (this.rows.has(candidate)) candidate = incrementRowid(candidate);
|
|
7881
8073
|
return candidate;
|
|
7882
8074
|
}
|
|
7883
8075
|
advanceNextRowid(rowid) {
|
|
8076
|
+
if (this.maximumRowid === null || this.maximumRowid !== void 0 && compareRowids(rowid, this.maximumRowid) > 0) {
|
|
8077
|
+
this.maximumRowid = rowid;
|
|
8078
|
+
}
|
|
7884
8079
|
if (compareRowids(rowid, this.nextRowid) >= 0) this.nextRowid = incrementRowid(rowid);
|
|
7885
8080
|
}
|
|
7886
8081
|
};
|
|
@@ -7970,6 +8165,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
7970
8165
|
changes = 0;
|
|
7971
8166
|
totalChanges = 0;
|
|
7972
8167
|
foreignKeysEnabled = false;
|
|
8168
|
+
/** When true, LIKE / like() are case-sensitive (SQLite `PRAGMA case_sensitive_like`). */
|
|
8169
|
+
caseSensitiveLike = false;
|
|
7973
8170
|
schemaVersion = 0;
|
|
7974
8171
|
userVersion = 0;
|
|
7975
8172
|
databaseForSchema(schema, qualifiedForError) {
|
|
@@ -8414,6 +8611,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8414
8611
|
copy.changes = this.changes;
|
|
8415
8612
|
copy.totalChanges = this.totalChanges;
|
|
8416
8613
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8614
|
+
copy.caseSensitiveLike = this.caseSensitiveLike;
|
|
8417
8615
|
copy.schemaVersion = this.schemaVersion;
|
|
8418
8616
|
copy.userVersion = this.userVersion;
|
|
8419
8617
|
return copy;
|
|
@@ -8433,6 +8631,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8433
8631
|
copy.changes = this.changes;
|
|
8434
8632
|
copy.totalChanges = this.totalChanges;
|
|
8435
8633
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8634
|
+
copy.caseSensitiveLike = this.caseSensitiveLike;
|
|
8436
8635
|
copy.schemaVersion = this.schemaVersion;
|
|
8437
8636
|
copy.userVersion = this.userVersion;
|
|
8438
8637
|
return copy;
|
|
@@ -8494,6 +8693,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8494
8693
|
this.changes = copy.changes;
|
|
8495
8694
|
this.totalChanges = copy.totalChanges;
|
|
8496
8695
|
this.foreignKeysEnabled = copy.foreignKeysEnabled;
|
|
8696
|
+
this.caseSensitiveLike = copy.caseSensitiveLike;
|
|
8497
8697
|
this.schemaVersion = copy.schemaVersion;
|
|
8498
8698
|
this.userVersion = copy.userVersion;
|
|
8499
8699
|
}
|
|
@@ -8996,8 +9196,9 @@ var ExecutionEnv = class {
|
|
|
8996
9196
|
createEvalContext(row = null, parent) {
|
|
8997
9197
|
const scope = row ?? this.triggerScope;
|
|
8998
9198
|
const cells = scope?.cells ?? [];
|
|
9199
|
+
const inheritedParent = parent ?? (row !== null && this.triggerScope !== null && row !== this.triggerScope ? this.createEvalContext(this.triggerScope) : void 0);
|
|
8999
9200
|
const context = {
|
|
9000
|
-
parent,
|
|
9201
|
+
parent: inheritedParent,
|
|
9001
9202
|
ftsMatch: scope?.ftsMatch ?? null,
|
|
9002
9203
|
functions: this.functions,
|
|
9003
9204
|
functionContext: {
|
|
@@ -9007,6 +9208,7 @@ var ExecutionEnv = class {
|
|
|
9007
9208
|
now: this.hooks.now,
|
|
9008
9209
|
random: this.hooks.random,
|
|
9009
9210
|
randomU64: this.hooks.randomU64,
|
|
9211
|
+
caseSensitiveLike: this.state.caseSensitiveLike,
|
|
9010
9212
|
ftsMatch: scope?.ftsMatch ?? null,
|
|
9011
9213
|
ftsRowid: scope?.rowid,
|
|
9012
9214
|
ftsSourceTable: scope?.sourceTable,
|
|
@@ -9038,8 +9240,13 @@ var ExecutionEnv = class {
|
|
|
9038
9240
|
if (table === null) return !cell.hiddenByUsing;
|
|
9039
9241
|
return (cell.tableLower ?? cell.table?.toLowerCase()) === tableKey;
|
|
9040
9242
|
});
|
|
9041
|
-
if (matches.length === 0)
|
|
9243
|
+
if (matches.length === 0) {
|
|
9244
|
+
if (table === null) {
|
|
9245
|
+
if (key === "true") return 1;
|
|
9246
|
+
if (key === "false") return 0;
|
|
9247
|
+
}
|
|
9042
9248
|
throw new SqliteError(`no such column: ${table ? `${table}.` : ""}${name}`, "no_such_column");
|
|
9249
|
+
}
|
|
9043
9250
|
if (matches.length > 1 && table === null) throw new SqliteError(`ambiguous column name: ${name}`, "other");
|
|
9044
9251
|
return matches[0].value;
|
|
9045
9252
|
},
|
|
@@ -9052,12 +9259,25 @@ var ExecutionEnv = class {
|
|
|
9052
9259
|
if (table === null) return !cell2.hiddenByUsing;
|
|
9053
9260
|
return (cell2.tableLower ?? cell2.table?.toLowerCase()) === tableKey;
|
|
9054
9261
|
});
|
|
9055
|
-
if (matches.length === 0)
|
|
9262
|
+
if (matches.length === 0) {
|
|
9263
|
+
if (table === null && (key === "true" || key === "false")) return "integer";
|
|
9056
9264
|
throw new SqliteError(`no such column: ${table ? `${table}.` : ""}${name}`, "no_such_column");
|
|
9265
|
+
}
|
|
9057
9266
|
if (matches.length > 1 && table === null) throw new SqliteError(`ambiguous column name: ${name}`, "other");
|
|
9058
9267
|
const cell = matches[0];
|
|
9059
9268
|
return cell.affinity === "REAL" && typeof cell.value === "number" ? "real" : storageClassOf(cell.value);
|
|
9060
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
|
+
},
|
|
9061
9281
|
resolveCollation: (table, name) => {
|
|
9062
9282
|
const key = name.toLowerCase();
|
|
9063
9283
|
const tableKey = table?.toLowerCase();
|
|
@@ -9465,6 +9685,8 @@ function queryPragma(name, args, env) {
|
|
|
9465
9685
|
switch (key) {
|
|
9466
9686
|
case "foreign_keys":
|
|
9467
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);
|
|
9468
9690
|
case "user_version":
|
|
9469
9691
|
return single("user_version", env.state.userVersion);
|
|
9470
9692
|
case "schema_version":
|
|
@@ -9892,83 +10114,6 @@ function hasTableValuedFunction(name) {
|
|
|
9892
10114
|
return hasRegisteredTableValuedFunction(name);
|
|
9893
10115
|
}
|
|
9894
10116
|
|
|
9895
|
-
// src/schema/catalog.ts
|
|
9896
|
-
function schemaCatalogRows(state) {
|
|
9897
|
-
const tables = [...state.tables.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9898
|
-
const indexes = [...state.indexes.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9899
|
-
const views = [...state.views.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9900
|
-
const triggers = [...state.triggers.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
9901
|
-
const rows = [];
|
|
9902
|
-
let rootpage = 2;
|
|
9903
|
-
for (const table of tables) {
|
|
9904
|
-
rows.push({
|
|
9905
|
-
type: "table",
|
|
9906
|
-
name: table.name,
|
|
9907
|
-
tbl_name: table.name,
|
|
9908
|
-
rootpage: rootpage++,
|
|
9909
|
-
sql: table.originalSql
|
|
9910
|
-
});
|
|
9911
|
-
}
|
|
9912
|
-
for (const index of indexes) {
|
|
9913
|
-
rows.push({
|
|
9914
|
-
type: "index",
|
|
9915
|
-
name: index.name,
|
|
9916
|
-
tbl_name: index.tableName,
|
|
9917
|
-
rootpage: rootpage++,
|
|
9918
|
-
sql: index.originalSql
|
|
9919
|
-
});
|
|
9920
|
-
}
|
|
9921
|
-
for (const view of views) {
|
|
9922
|
-
rows.push({
|
|
9923
|
-
type: "view",
|
|
9924
|
-
name: view.name,
|
|
9925
|
-
tbl_name: view.name,
|
|
9926
|
-
rootpage: 0,
|
|
9927
|
-
sql: view.originalSql
|
|
9928
|
-
});
|
|
9929
|
-
}
|
|
9930
|
-
for (const trigger of triggers) {
|
|
9931
|
-
rows.push({
|
|
9932
|
-
type: "trigger",
|
|
9933
|
-
name: trigger.name,
|
|
9934
|
-
tbl_name: trigger.tableName,
|
|
9935
|
-
rootpage: 0,
|
|
9936
|
-
sql: trigger.originalSql
|
|
9937
|
-
});
|
|
9938
|
-
}
|
|
9939
|
-
return rows;
|
|
9940
|
-
}
|
|
9941
|
-
function compareNames2(a, b) {
|
|
9942
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
9943
|
-
}
|
|
9944
|
-
function buildSchemaCatalog(state, name = "sqlite_schema") {
|
|
9945
|
-
const catalog = new Table(name, [
|
|
9946
|
-
makeColumnInfo("type", "TEXT"),
|
|
9947
|
-
makeColumnInfo("name", "TEXT"),
|
|
9948
|
-
makeColumnInfo("tbl_name", "TEXT"),
|
|
9949
|
-
makeColumnInfo("rootpage", "INTEGER"),
|
|
9950
|
-
makeColumnInfo("sql", "TEXT")
|
|
9951
|
-
]);
|
|
9952
|
-
for (const row of schemaCatalogRows(state)) {
|
|
9953
|
-
catalog.insert({
|
|
9954
|
-
values: {
|
|
9955
|
-
type: row.type,
|
|
9956
|
-
name: row.name,
|
|
9957
|
-
tbl_name: row.tbl_name,
|
|
9958
|
-
rootpage: row.rootpage,
|
|
9959
|
-
sql: row.sql
|
|
9960
|
-
}
|
|
9961
|
-
});
|
|
9962
|
-
}
|
|
9963
|
-
return catalog;
|
|
9964
|
-
}
|
|
9965
|
-
function buildSqliteSchema(state) {
|
|
9966
|
-
return buildSchemaCatalog(state, "sqlite_schema");
|
|
9967
|
-
}
|
|
9968
|
-
function buildSqliteMaster(state) {
|
|
9969
|
-
return buildSchemaCatalog(state, "sqlite_master");
|
|
9970
|
-
}
|
|
9971
|
-
|
|
9972
10117
|
// src/expressions/equals.ts
|
|
9973
10118
|
function exprEquals(left, right) {
|
|
9974
10119
|
if (left.type !== right.type) return false;
|
|
@@ -9981,6 +10126,8 @@ function exprEquals(left, right) {
|
|
|
9981
10126
|
return right.type === "column" && (left.table ?? "").toLowerCase() === (right.table ?? "").toLowerCase() && left.name.toLowerCase() === right.name.toLowerCase();
|
|
9982
10127
|
case "unary":
|
|
9983
10128
|
return right.type === "unary" && left.op === right.op && exprEquals(left.expr, right.expr);
|
|
10129
|
+
case "is_bool":
|
|
10130
|
+
return right.type === "is_bool" && left.not === right.not && left.sense === right.sense && exprEquals(left.expr, right.expr);
|
|
9984
10131
|
case "binary":
|
|
9985
10132
|
return right.type === "binary" && left.op === right.op && exprEquals(left.left, right.left) && exprEquals(left.right, right.right);
|
|
9986
10133
|
case "function":
|
|
@@ -10360,6 +10507,83 @@ function columnOnTable(column, alias, tableName) {
|
|
|
10360
10507
|
return column.table !== null && matchesTable(column.table, alias, tableName);
|
|
10361
10508
|
}
|
|
10362
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
|
+
|
|
10363
10587
|
// src/executor/simple-select.ts
|
|
10364
10588
|
function tryExecuteSimpleSelect(stmt, env) {
|
|
10365
10589
|
if (stmt.with || stmt.compound || stmt.distinct || stmt.groupBy.length > 0 || stmt.having || stmt.windows.length > 0)
|
|
@@ -10408,7 +10632,9 @@ function tryExecuteSimpleSelect(stmt, env) {
|
|
|
10408
10632
|
} catch {
|
|
10409
10633
|
return null;
|
|
10410
10634
|
}
|
|
10411
|
-
|
|
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 });
|
|
10412
10638
|
}
|
|
10413
10639
|
}
|
|
10414
10640
|
let limit = env.maxRows;
|
|
@@ -10590,7 +10816,7 @@ function executeSelect2(stmt, env, parent) {
|
|
|
10590
10816
|
}
|
|
10591
10817
|
const savedCtes = new Map(env.ctes);
|
|
10592
10818
|
try {
|
|
10593
|
-
if (stmt.with) executeWith(stmt, env, parent);
|
|
10819
|
+
if (stmt.with) executeWith(stmt.with, env, parent);
|
|
10594
10820
|
const base = executeSelectCore(
|
|
10595
10821
|
{
|
|
10596
10822
|
...stmt,
|
|
@@ -10646,22 +10872,39 @@ function executeSelect2(stmt, env, parent) {
|
|
|
10646
10872
|
for (const [name, result] of savedCtes) env.ctes.set(name, result);
|
|
10647
10873
|
}
|
|
10648
10874
|
}
|
|
10649
|
-
function executeWith(
|
|
10650
|
-
for (const cte of
|
|
10875
|
+
function executeWith(withClause, env, parent) {
|
|
10876
|
+
for (const cte of withClause.ctes) {
|
|
10651
10877
|
const key = cte.name.toLowerCase();
|
|
10652
|
-
if (
|
|
10878
|
+
if (withClause.recursive && referencesTable(cte.select, cte.name) && cte.select.compound) {
|
|
10653
10879
|
const anchor = executeSelect2({ ...cte.select, compound: null }, env, parent);
|
|
10654
10880
|
const columns = cte.columns ?? anchor.columns;
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
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);
|
|
10660
10896
|
const candidates = resultValues(nextResult);
|
|
10661
|
-
const additions =
|
|
10662
|
-
|
|
10663
|
-
|
|
10664
|
-
|
|
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
|
+
}
|
|
10665
10908
|
}
|
|
10666
10909
|
env.ctes.set(key, valuesToResult(columns, accumulated));
|
|
10667
10910
|
} else {
|
|
@@ -10671,6 +10914,26 @@ function executeWith(stmt, env, parent) {
|
|
|
10671
10914
|
}
|
|
10672
10915
|
}
|
|
10673
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
|
+
}
|
|
10674
10937
|
function executeSelectCore(stmt, env, parent) {
|
|
10675
10938
|
let scopes;
|
|
10676
10939
|
let skipWhere = false;
|
|
@@ -10726,10 +10989,18 @@ function executeSelectCore(stmt, env, parent) {
|
|
|
10726
10989
|
}
|
|
10727
10990
|
}
|
|
10728
10991
|
const groupBy = stmt.groupBy.map((expr) => {
|
|
10729
|
-
if (expr.type
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
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;
|
|
10733
11004
|
});
|
|
10734
11005
|
const groups = aggregate ? groupRows(scopes, groupBy, env, parent) : scopes.map((scope) => [scope]);
|
|
10735
11006
|
const windowScopes = aggregate ? groups.map((group) => group[0] ?? { cells: [] }) : scopes;
|
|
@@ -11186,7 +11457,10 @@ function groupRows(rows, expressions, env, parent) {
|
|
|
11186
11457
|
const groups = [];
|
|
11187
11458
|
const indexByKey = /* @__PURE__ */ new Map();
|
|
11188
11459
|
for (const row of rows) {
|
|
11189
|
-
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
|
+
});
|
|
11190
11464
|
const key = valueKey(keyValues);
|
|
11191
11465
|
const index = indexByKey.get(key);
|
|
11192
11466
|
if (index === void 0) {
|
|
@@ -11209,7 +11483,8 @@ function aggregateValue(expr, rows, env, parent) {
|
|
|
11209
11483
|
const accumulator = env.functions.createAggregate(expr.name);
|
|
11210
11484
|
if (!accumulator) throw new SqliteError(`no such aggregate function: ${expr.name}`, "other");
|
|
11211
11485
|
const seen = [];
|
|
11212
|
-
|
|
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) {
|
|
11213
11488
|
const ctx = env.createEvalContext(row, parent);
|
|
11214
11489
|
if (expr.filter && isTruthySql(evalExpr(expr.filter, ctx)) !== true) continue;
|
|
11215
11490
|
const args = expr.args === "*" ? [] : expr.args.map((arg) => evalExpr(arg, ctx));
|
|
@@ -11325,8 +11600,10 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
|
11325
11600
|
if (!spec.frame) return [0, spec.orderBy.length > 0 ? defaultFrameEnd : Math.max(0, length - 1)];
|
|
11326
11601
|
const isRangeLike = spec.frame.type === "RANGE" || spec.frame.type === "GROUPS";
|
|
11327
11602
|
let peerFirst = index;
|
|
11603
|
+
let peerLast = index;
|
|
11328
11604
|
if (isRangeLike && spec.orderBy.length > 0) {
|
|
11329
11605
|
while (peerFirst > 0 && rowsEqual2(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
|
|
11606
|
+
while (peerLast + 1 < length && rowsEqual2(orderKeys[peerLast], orderKeys[peerLast + 1])) peerLast++;
|
|
11330
11607
|
}
|
|
11331
11608
|
const bound = (item, isStart) => {
|
|
11332
11609
|
switch (item.kind) {
|
|
@@ -11335,12 +11612,54 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
|
11335
11612
|
case "unbounded_following":
|
|
11336
11613
|
return Math.max(0, length - 1);
|
|
11337
11614
|
case "current_row":
|
|
11338
|
-
if (isRangeLike) return isStart ? peerFirst :
|
|
11615
|
+
if (isRangeLike) return isStart ? peerFirst : peerLast;
|
|
11339
11616
|
return index;
|
|
11340
11617
|
case "preceding":
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
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
|
+
}
|
|
11344
11663
|
}
|
|
11345
11664
|
};
|
|
11346
11665
|
return [bound(spec.frame.start, true), bound(spec.frame.end, false)];
|
|
@@ -11445,6 +11764,9 @@ function validateProjectedColumns(stmt, sample, env, parent) {
|
|
|
11445
11764
|
case "unary":
|
|
11446
11765
|
recurse(expr.expr);
|
|
11447
11766
|
break;
|
|
11767
|
+
case "is_bool":
|
|
11768
|
+
recurse(expr.expr);
|
|
11769
|
+
break;
|
|
11448
11770
|
case "binary":
|
|
11449
11771
|
recurse(expr.left);
|
|
11450
11772
|
recurse(expr.right);
|
|
@@ -11507,6 +11829,10 @@ function expressionName(expr) {
|
|
|
11507
11829
|
if (expr.type === "binary") {
|
|
11508
11830
|
return `${expressionName(expr.left)} ${expr.op} ${expressionName(expr.right)}`;
|
|
11509
11831
|
}
|
|
11832
|
+
if (expr.type === "is_bool") {
|
|
11833
|
+
const sense = expr.sense ? "TRUE" : "FALSE";
|
|
11834
|
+
return `${expressionName(expr.expr)} IS${expr.not ? " NOT" : ""} ${sense}`;
|
|
11835
|
+
}
|
|
11510
11836
|
return expr.type;
|
|
11511
11837
|
}
|
|
11512
11838
|
function replaceSpecial(expr, evaluate) {
|
|
@@ -11515,6 +11841,8 @@ function replaceSpecial(expr, evaluate) {
|
|
|
11515
11841
|
switch (expr.type) {
|
|
11516
11842
|
case "unary":
|
|
11517
11843
|
return { ...expr, expr: recurse(expr.expr) };
|
|
11844
|
+
case "is_bool":
|
|
11845
|
+
return { ...expr, expr: recurse(expr.expr) };
|
|
11518
11846
|
case "binary":
|
|
11519
11847
|
return { ...expr, left: recurse(expr.left), right: recurse(expr.right) };
|
|
11520
11848
|
case "between":
|
|
@@ -11958,6 +12286,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
|
|
|
11958
12286
|
}
|
|
11959
12287
|
env.triggerDepth++;
|
|
11960
12288
|
const savedScope = env.triggerScope;
|
|
12289
|
+
const savedLastInsertRowid = env.state.lastInsertRowid;
|
|
11961
12290
|
env.triggerScope = triggerScope(table, oldRow, newValues);
|
|
11962
12291
|
try {
|
|
11963
12292
|
for (const statement of trigger.body) {
|
|
@@ -11975,6 +12304,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
|
|
|
11975
12304
|
}
|
|
11976
12305
|
throw error;
|
|
11977
12306
|
} finally {
|
|
12307
|
+
env.state.lastInsertRowid = savedLastInsertRowid;
|
|
11978
12308
|
env.triggerScope = savedScope;
|
|
11979
12309
|
env.triggerDepth--;
|
|
11980
12310
|
}
|
|
@@ -12069,9 +12399,20 @@ function storeColumnValue(table, column, value) {
|
|
|
12069
12399
|
return applyAffinity(value, column.affinity);
|
|
12070
12400
|
}
|
|
12071
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) {
|
|
12072
12410
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12073
12411
|
return executeVirtualInsert(stmt, env);
|
|
12074
12412
|
}
|
|
12413
|
+
const totalBefore = env.state.totalChanges;
|
|
12414
|
+
const view = writableView(stmt.table, "INSERT", env);
|
|
12415
|
+
if (view) return executeViewInsert(stmt, view, env, totalBefore);
|
|
12075
12416
|
const fast = tryFastInsert(stmt, env);
|
|
12076
12417
|
if (fast) return fast;
|
|
12077
12418
|
const table = env.state.getWritableTable(stmt.table);
|
|
@@ -12089,11 +12430,14 @@ function executeInsert(stmt, env) {
|
|
|
12089
12430
|
const suppliedIndexes = table.columns.map(
|
|
12090
12431
|
(column) => columnNames.findIndex((name) => name.toLowerCase() === (column.nameLower ?? column.name.toLowerCase()))
|
|
12091
12432
|
);
|
|
12092
|
-
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;
|
|
12093
12434
|
if (unconstrained) {
|
|
12094
12435
|
for (const source of sourceRows) {
|
|
12095
12436
|
if (source.length !== columnNames.length)
|
|
12096
|
-
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
|
+
);
|
|
12097
12441
|
const values = /* @__PURE__ */ new Map();
|
|
12098
12442
|
for (let index = 0; index < table.columns.length; index++) {
|
|
12099
12443
|
const column = table.columns[index];
|
|
@@ -12109,7 +12453,10 @@ function executeInsert(stmt, env) {
|
|
|
12109
12453
|
}
|
|
12110
12454
|
for (const source of sourceRows) {
|
|
12111
12455
|
if (source.length !== columnNames.length)
|
|
12112
|
-
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
|
+
);
|
|
12113
12460
|
const values = /* @__PURE__ */ new Map();
|
|
12114
12461
|
for (const column of table.columns) {
|
|
12115
12462
|
if (column.generated) continue;
|
|
@@ -12197,9 +12544,12 @@ function executeInsert(stmt, env) {
|
|
|
12197
12544
|
validateRow(table, row, env);
|
|
12198
12545
|
if (table.indexes.length > 0) addIndexes(table, row, env);
|
|
12199
12546
|
if (env.state.foreignKeysEnabled) checkForeignKeys(table, row, env);
|
|
12547
|
+
if (!table.withoutRowid) {
|
|
12548
|
+
last = rowid;
|
|
12549
|
+
env.state.lastInsertRowid = rowid;
|
|
12550
|
+
}
|
|
12200
12551
|
fireInsertTriggers("AFTER", table, row.values, null, env);
|
|
12201
12552
|
changes++;
|
|
12202
|
-
last = table.withoutRowid ? 0 : rowid;
|
|
12203
12553
|
if (stmt.returning.length)
|
|
12204
12554
|
returningRows.push(projectReturning(stmt.returning, scopeFor(table, row, stmt.table, env), env));
|
|
12205
12555
|
} catch (error) {
|
|
@@ -12212,12 +12562,12 @@ function executeInsert(stmt, env) {
|
|
|
12212
12562
|
throw error;
|
|
12213
12563
|
}
|
|
12214
12564
|
}
|
|
12215
|
-
|
|
12216
|
-
if (stmt.returning.length === 0) return emptyResult(
|
|
12217
|
-
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);
|
|
12218
12568
|
}
|
|
12219
12569
|
function evaluateInsertSource(stmt, env) {
|
|
12220
|
-
if (stmt.select) return resultValues(executeSelect2(
|
|
12570
|
+
if (stmt.select) return resultValues(executeSelect2(stmt.select, env));
|
|
12221
12571
|
if (!stmt.values) return [[]];
|
|
12222
12572
|
const ctx = env.createEvalContext();
|
|
12223
12573
|
return stmt.values.map(
|
|
@@ -12246,6 +12596,7 @@ function tryFastInsert(stmt, env) {
|
|
|
12246
12596
|
} catch {
|
|
12247
12597
|
return null;
|
|
12248
12598
|
}
|
|
12599
|
+
if (env.state.databaseForTable(table).triggers.size > 0) return null;
|
|
12249
12600
|
if (!table.isUnconstrained()) return null;
|
|
12250
12601
|
const values = /* @__PURE__ */ new Map();
|
|
12251
12602
|
for (const slot of plan.slots) values.set(slot.key, applyAffinity(slot.read(env), slot.affinity));
|
|
@@ -12290,9 +12641,20 @@ function buildFastInsertPlan(stmt, env) {
|
|
|
12290
12641
|
return { tableName: stmt.table, slots };
|
|
12291
12642
|
}
|
|
12292
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) {
|
|
12293
12652
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12294
12653
|
return executeVirtualUpdate(stmt, env);
|
|
12295
12654
|
}
|
|
12655
|
+
const totalBefore = env.state.totalChanges;
|
|
12656
|
+
const view = writableView(stmt.table, "UPDATE", env);
|
|
12657
|
+
if (view) return executeViewUpdate(stmt, view, env, totalBefore);
|
|
12296
12658
|
const table = env.state.getWritableTable(stmt.table);
|
|
12297
12659
|
const alias = stmt.alias ?? stmt.table;
|
|
12298
12660
|
const candidates = [];
|
|
@@ -12345,13 +12707,24 @@ function executeUpdate(stmt, env) {
|
|
|
12345
12707
|
throw error;
|
|
12346
12708
|
}
|
|
12347
12709
|
}
|
|
12348
|
-
|
|
12349
|
-
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
|
+
);
|
|
12350
12717
|
}
|
|
12351
12718
|
function executeDelete(stmt, env) {
|
|
12719
|
+
return withDmlCtes(stmt.with, env, () => executeDeleteCore(stmt, env));
|
|
12720
|
+
}
|
|
12721
|
+
function executeDeleteCore(stmt, env) {
|
|
12352
12722
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12353
12723
|
return executeVirtualDelete(stmt, env);
|
|
12354
12724
|
}
|
|
12725
|
+
const totalBefore = env.state.totalChanges;
|
|
12726
|
+
const view = writableView(stmt.table, "DELETE", env);
|
|
12727
|
+
if (view) return executeViewDelete(stmt, view, env, totalBefore);
|
|
12355
12728
|
const table = env.state.getWritableTable(stmt.table);
|
|
12356
12729
|
const selectedSource = stmt.where === null ? null : tryIndexedTableRows({ type: "table", schema: null, name: stmt.table, alias: stmt.alias }, stmt.where, env);
|
|
12357
12730
|
const selected = [...selectedSource ? selectedSource.rows : table.scan()].filter((row) => {
|
|
@@ -12369,8 +12742,120 @@ function executeDelete(stmt, env) {
|
|
|
12369
12742
|
fireDeleteTriggers("AFTER", table, row, env);
|
|
12370
12743
|
changes++;
|
|
12371
12744
|
}
|
|
12372
|
-
|
|
12373
|
-
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;
|
|
12374
12859
|
}
|
|
12375
12860
|
function mergedValues(table, row, updates) {
|
|
12376
12861
|
const values = /* @__PURE__ */ new Map();
|
|
@@ -12681,7 +13166,7 @@ function applyReferentialDelete(parent, row, env) {
|
|
|
12681
13166
|
} else if (constraint.onDelete === "SET DEFAULT") {
|
|
12682
13167
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
12683
13168
|
changes += 1 + updated.cascaded;
|
|
12684
|
-
} else if (!fkIsDeferred(constraint, env)) {
|
|
13169
|
+
} else if (constraint.onDelete === "RESTRICT" || !fkIsDeferred(constraint, env)) {
|
|
12685
13170
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12686
13171
|
}
|
|
12687
13172
|
}
|
|
@@ -12724,7 +13209,7 @@ function applyReferentialUpdate(parent, before, after, env) {
|
|
|
12724
13209
|
} else if (constraint.onUpdate === "SET DEFAULT") {
|
|
12725
13210
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
12726
13211
|
changes += 1 + updated.cascaded;
|
|
12727
|
-
} else if (!fkIsDeferred(constraint, env)) {
|
|
13212
|
+
} else if (constraint.onUpdate === "RESTRICT" || !fkIsDeferred(constraint, env)) {
|
|
12728
13213
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12729
13214
|
}
|
|
12730
13215
|
}
|
|
@@ -13005,6 +13490,11 @@ function executePragma(name, expr, env) {
|
|
|
13005
13490
|
if (!env.transactions.inTransaction) env.state.foreignKeysEnabled = coercePragmaTruthy(value);
|
|
13006
13491
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
13007
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
|
+
}
|
|
13008
13498
|
if ((key === "user_version" || key === "schema_version") && expr !== null) {
|
|
13009
13499
|
const value = evalPragmaSetValue(expr, env);
|
|
13010
13500
|
const num2 = coercePragmaInt(value);
|
|
@@ -13217,7 +13707,10 @@ var Statement = class _Statement {
|
|
|
13217
13707
|
execute(params, options) {
|
|
13218
13708
|
this.database.assertOpen();
|
|
13219
13709
|
this.reprepareIfSchemaChanged();
|
|
13220
|
-
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
|
+
}
|
|
13221
13714
|
this.namedPlan ??= planNamedParameters(this.sql);
|
|
13222
13715
|
const expected = this.namedPlan.expectedCount;
|
|
13223
13716
|
if (params.length > 0 && params.length !== expected) {
|
|
@@ -13289,11 +13782,18 @@ function planNamedParameters(sql) {
|
|
|
13289
13782
|
var Database = class {
|
|
13290
13783
|
/** @internal Engine catalog, tables, and mutation counters. */
|
|
13291
13784
|
state = new DatabaseState();
|
|
13292
|
-
/** Seed used to construct the PRNG. */
|
|
13785
|
+
/** Seed used to construct the PRNG. Ignored when {@link randomMode} is `"os"`. */
|
|
13293
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;
|
|
13294
13794
|
/**
|
|
13295
13795
|
* PRNG backing `random()` / `randomblob()` and related builtins.
|
|
13296
|
-
* Prefer passing `seed` to the constructor.
|
|
13796
|
+
* Prefer passing `seed` / `random` to the constructor.
|
|
13297
13797
|
* @internal
|
|
13298
13798
|
*/
|
|
13299
13799
|
prng;
|
|
@@ -13316,7 +13816,9 @@ var Database = class {
|
|
|
13316
13816
|
*/
|
|
13317
13817
|
constructor(options = {}) {
|
|
13318
13818
|
this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
|
|
13319
|
-
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);
|
|
13320
13822
|
this.now = resolveClock(options.now);
|
|
13321
13823
|
this.transactions = new TransactionManager(this.state, this.prng);
|
|
13322
13824
|
}
|
|
@@ -13430,7 +13932,7 @@ var Database = class {
|
|
|
13430
13932
|
this.state.replaceWith(decoded.state, { adopt: true });
|
|
13431
13933
|
if (decoded.runtime) {
|
|
13432
13934
|
this.prng.setState(decoded.runtime.prngState);
|
|
13433
|
-
this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
13935
|
+
if (!this.systemClock) this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
13434
13936
|
}
|
|
13435
13937
|
}
|
|
13436
13938
|
/**
|
|
@@ -13465,6 +13967,15 @@ var Database = class {
|
|
|
13465
13967
|
this.assertOpen();
|
|
13466
13968
|
return this.state.lastInsertRowid;
|
|
13467
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
|
+
}
|
|
13468
13979
|
/**
|
|
13469
13980
|
* Throw if {@link close} has already been called.
|
|
13470
13981
|
* @internal
|