@crvouga/sqlite-mem 1.1.2 → 1.3.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 +26 -15
- 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 +2 -1
- package/dist/ast/nodes.d.ts +3 -0
- package/dist/executor/env.d.ts +3 -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 +790 -211
- package/dist/index.js.map +4 -4
- package/dist/parser/index.d.ts +7 -1
- package/dist/parser/parser.d.ts +14 -4
- 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/schema/master-sql.d.ts +10 -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 +263 -72
- package/dist/unstable.js.map +3 -3
- package/dist/vtable/fts/table.d.ts +2 -3
- package/package.json +10 -3
package/dist/index.js
CHANGED
|
@@ -640,10 +640,12 @@ var PREC = {
|
|
|
640
640
|
JSON_ARROW: 80
|
|
641
641
|
};
|
|
642
642
|
var Parser = class {
|
|
643
|
-
constructor(tokens) {
|
|
643
|
+
constructor(tokens, source = "") {
|
|
644
644
|
this.tokens = tokens;
|
|
645
|
+
this.source = source;
|
|
645
646
|
}
|
|
646
647
|
tokens;
|
|
648
|
+
source;
|
|
647
649
|
pos = 0;
|
|
648
650
|
current() {
|
|
649
651
|
return this.tokens[this.pos] ?? this.tokens[this.tokens.length - 1];
|
|
@@ -727,13 +729,21 @@ var Parser = class {
|
|
|
727
729
|
}
|
|
728
730
|
// ── Statements ──────────────────────────────────────────────────────────
|
|
729
731
|
parseStatements() {
|
|
730
|
-
|
|
732
|
+
return this.parseUnits().map((unit) => unit.statement);
|
|
733
|
+
}
|
|
734
|
+
/** Parse statements with per-statement source slices for catalog `sql` text. */
|
|
735
|
+
parseUnits() {
|
|
736
|
+
const units = [];
|
|
731
737
|
while (!this.at("EOF")) {
|
|
732
738
|
if (this.match("SEMI")) continue;
|
|
733
|
-
|
|
739
|
+
const start = this.current().start;
|
|
740
|
+
const statement = this.parseStatement();
|
|
741
|
+
const end = this.pos > 0 ? this.tokens[this.pos - 1].end : this.current().end;
|
|
734
742
|
this.match("SEMI");
|
|
743
|
+
const sql = this.source ? this.source.slice(start, end).trimEnd() : "";
|
|
744
|
+
units.push({ statement, sql });
|
|
735
745
|
}
|
|
736
|
-
return
|
|
746
|
+
return units;
|
|
737
747
|
}
|
|
738
748
|
parseStatement() {
|
|
739
749
|
if (this.match("EXPLAIN")) {
|
|
@@ -745,13 +755,20 @@ var Parser = class {
|
|
|
745
755
|
const stmt = this.parseStatement();
|
|
746
756
|
return { type: "explain", queryPlan, statement: stmt };
|
|
747
757
|
}
|
|
748
|
-
if (this.
|
|
758
|
+
if (this.at("WITH")) {
|
|
759
|
+
const withClause = this.parseWithClause();
|
|
760
|
+
if (this.at("SELECT")) return this.parseSelectStmt(withClause);
|
|
761
|
+
if (this.at("INSERT") || this.at("REPLACE")) return this.parseInsertStmt(withClause);
|
|
762
|
+
if (this.at("UPDATE")) return this.parseUpdateStmt(withClause);
|
|
763
|
+
if (this.at("DELETE")) return this.parseDeleteStmt(withClause);
|
|
764
|
+
this.syntaxError("expected SELECT, INSERT, UPDATE, or DELETE after WITH clause");
|
|
765
|
+
}
|
|
749
766
|
if (this.at("SELECT")) return this.parseSelectStmt();
|
|
750
|
-
if (this.
|
|
767
|
+
if (this.at("INSERT") || this.at("REPLACE")) {
|
|
751
768
|
return this.parseInsertStmt();
|
|
752
769
|
}
|
|
753
|
-
if (this.
|
|
754
|
-
if (this.
|
|
770
|
+
if (this.at("UPDATE")) return this.parseUpdateStmt();
|
|
771
|
+
if (this.at("DELETE")) return this.parseDeleteStmt();
|
|
755
772
|
if (this.at("CREATE")) return this.parseCreateStmt();
|
|
756
773
|
if (this.at("DROP")) return this.parseDropStmt();
|
|
757
774
|
if (this.at("ALTER")) return this.parseAlterTableStmt();
|
|
@@ -813,12 +830,15 @@ var Parser = class {
|
|
|
813
830
|
this.expect("RPAREN");
|
|
814
831
|
}
|
|
815
832
|
this.expect("AS");
|
|
816
|
-
|
|
817
|
-
|
|
833
|
+
let materialized = null;
|
|
834
|
+
if (this.match("NOT")) {
|
|
835
|
+
this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
|
|
836
|
+
materialized = "not_materialized";
|
|
837
|
+
} else if (this.match("MATERIALIZED")) materialized = "materialized";
|
|
818
838
|
this.expect("LPAREN");
|
|
819
|
-
const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.parseSelectCore();
|
|
839
|
+
const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.at("WITH") ? this.parseSelectStmt() : this.parseSelectCore();
|
|
820
840
|
this.expect("RPAREN");
|
|
821
|
-
ctes.push({ name, columns, select });
|
|
841
|
+
ctes.push({ name, columns, materialized, select });
|
|
822
842
|
} while (this.match("COMMA"));
|
|
823
843
|
return { recursive, ctes };
|
|
824
844
|
}
|
|
@@ -826,8 +846,7 @@ var Parser = class {
|
|
|
826
846
|
return this.at("WITH") ? this.parseWithClause() : null;
|
|
827
847
|
}
|
|
828
848
|
// ── SELECT ──────────────────────────────────────────────────────────────
|
|
829
|
-
parseSelectStmt() {
|
|
830
|
-
const withClause = this.parseOptionalWith();
|
|
849
|
+
parseSelectStmt(withClause = this.parseOptionalWith()) {
|
|
831
850
|
const select = this.parseSelectCore();
|
|
832
851
|
select.with = withClause;
|
|
833
852
|
return select;
|
|
@@ -1125,8 +1144,7 @@ var Parser = class {
|
|
|
1125
1144
|
return { limit: first, offset };
|
|
1126
1145
|
}
|
|
1127
1146
|
// ── INSERT / REPLACE ────────────────────────────────────────────────────
|
|
1128
|
-
parseInsertStmt() {
|
|
1129
|
-
const withClause = this.parseOptionalWith();
|
|
1147
|
+
parseInsertStmt(withClause = this.parseOptionalWith()) {
|
|
1130
1148
|
let mode = "insert";
|
|
1131
1149
|
if (this.match("REPLACE")) {
|
|
1132
1150
|
mode = "replace";
|
|
@@ -1229,8 +1247,7 @@ var Parser = class {
|
|
|
1229
1247
|
return this.parseResultColumns();
|
|
1230
1248
|
}
|
|
1231
1249
|
// ── UPDATE ──────────────────────────────────────────────────────────────
|
|
1232
|
-
parseUpdateStmt() {
|
|
1233
|
-
const withClause = this.parseOptionalWith();
|
|
1250
|
+
parseUpdateStmt(withClause = this.parseOptionalWith()) {
|
|
1234
1251
|
this.expect("UPDATE");
|
|
1235
1252
|
const or = this.mapUpdateOr(this.parseOrConflict());
|
|
1236
1253
|
const table = this.parseTableName();
|
|
@@ -1245,8 +1262,7 @@ var Parser = class {
|
|
|
1245
1262
|
return { type: "update", with: withClause, or, table, alias, set, from, where, returning };
|
|
1246
1263
|
}
|
|
1247
1264
|
// ── DELETE ──────────────────────────────────────────────────────────────
|
|
1248
|
-
parseDeleteStmt() {
|
|
1249
|
-
const withClause = this.parseOptionalWith();
|
|
1265
|
+
parseDeleteStmt(withClause = this.parseOptionalWith()) {
|
|
1250
1266
|
this.expect("DELETE");
|
|
1251
1267
|
this.expect("FROM");
|
|
1252
1268
|
const table = this.parseTableName();
|
|
@@ -1915,6 +1931,24 @@ var Parser = class {
|
|
|
1915
1931
|
left = this.parseLikeRhs(left, true, "GLOB");
|
|
1916
1932
|
continue;
|
|
1917
1933
|
}
|
|
1934
|
+
if (n.kind === "REGEXP") {
|
|
1935
|
+
this.advance();
|
|
1936
|
+
this.advance();
|
|
1937
|
+
const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
|
|
1938
|
+
left = {
|
|
1939
|
+
type: "unary",
|
|
1940
|
+
op: "NOT",
|
|
1941
|
+
expr: {
|
|
1942
|
+
type: "function",
|
|
1943
|
+
name: "REGEXP",
|
|
1944
|
+
distinct: false,
|
|
1945
|
+
args: [pattern, left],
|
|
1946
|
+
orderBy: [],
|
|
1947
|
+
filter: null
|
|
1948
|
+
}
|
|
1949
|
+
};
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1918
1952
|
if (n.kind === "BETWEEN") {
|
|
1919
1953
|
this.advance();
|
|
1920
1954
|
this.advance();
|
|
@@ -1970,6 +2004,12 @@ var Parser = class {
|
|
|
1970
2004
|
left = this.parseLikeRhs(left, false, "GLOB");
|
|
1971
2005
|
continue;
|
|
1972
2006
|
}
|
|
2007
|
+
if (this.at("REGEXP") && PREC.IS_IN_LIKE >= minPrec) {
|
|
2008
|
+
this.advance();
|
|
2009
|
+
const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
|
|
2010
|
+
left = { type: "function", name: "REGEXP", distinct: false, args: [pattern, left], orderBy: [], filter: null };
|
|
2011
|
+
continue;
|
|
2012
|
+
}
|
|
1973
2013
|
if (this.at("MATCH") && PREC.IS_IN_LIKE >= minPrec) {
|
|
1974
2014
|
this.advance();
|
|
1975
2015
|
const right2 = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
|
|
@@ -2128,7 +2168,7 @@ var Parser = class {
|
|
|
2128
2168
|
}
|
|
2129
2169
|
if (this.at("CURRENT_DATE") || this.at("CURRENT_TIME") || this.at("CURRENT_TIMESTAMP")) {
|
|
2130
2170
|
const tok = this.advance();
|
|
2131
|
-
return { type: "function", name: tok.value, distinct: false, args: [], filter: null };
|
|
2171
|
+
return { type: "function", name: tok.value, distinct: false, args: [], orderBy: [], filter: null };
|
|
2132
2172
|
}
|
|
2133
2173
|
if (this.at("NUMBER")) {
|
|
2134
2174
|
const tok = this.advance();
|
|
@@ -2211,6 +2251,7 @@ var Parser = class {
|
|
|
2211
2251
|
} while (this.match("COMMA"));
|
|
2212
2252
|
}
|
|
2213
2253
|
}
|
|
2254
|
+
const orderBy = this.parseOrderBy();
|
|
2214
2255
|
this.expect("RPAREN");
|
|
2215
2256
|
if (this.match("FILTER")) {
|
|
2216
2257
|
this.expect("LPAREN");
|
|
@@ -2231,13 +2272,13 @@ var Parser = class {
|
|
|
2231
2272
|
window = this.parseWindowSpec();
|
|
2232
2273
|
this.expect("RPAREN");
|
|
2233
2274
|
}
|
|
2234
|
-
const func = isAgg ? { type: "aggregate", name: upper, distinct, args, filter } : { type: "function", name, distinct, args, filter };
|
|
2275
|
+
const func = isAgg ? { type: "aggregate", name: upper, distinct, args, orderBy, filter } : { type: "function", name, distinct, args, orderBy, filter };
|
|
2235
2276
|
return { type: "window", func, window };
|
|
2236
2277
|
}
|
|
2237
2278
|
if (isAgg) {
|
|
2238
|
-
return { type: "aggregate", name: upper, distinct, args, filter };
|
|
2279
|
+
return { type: "aggregate", name: upper, distinct, args, orderBy, filter };
|
|
2239
2280
|
}
|
|
2240
|
-
return { type: "function", name, distinct, args, filter };
|
|
2281
|
+
return { type: "function", name, distinct, args, orderBy, filter };
|
|
2241
2282
|
}
|
|
2242
2283
|
parseCaseExpr(base) {
|
|
2243
2284
|
const whens = [];
|
|
@@ -2328,14 +2369,13 @@ var Parser = class {
|
|
|
2328
2369
|
return { kind: "following", expr };
|
|
2329
2370
|
}
|
|
2330
2371
|
};
|
|
2331
|
-
function
|
|
2332
|
-
return new Parser(tokens).
|
|
2372
|
+
function parseTokenUnits(tokens, source) {
|
|
2373
|
+
return new Parser(tokens, source).parseUnits();
|
|
2333
2374
|
}
|
|
2334
2375
|
|
|
2335
2376
|
// src/parser/index.ts
|
|
2336
|
-
function
|
|
2337
|
-
|
|
2338
|
-
return parseTokens(tokens);
|
|
2377
|
+
function parseUnits(sql) {
|
|
2378
|
+
return parseTokenUnits(tokenize(sql), sql);
|
|
2339
2379
|
}
|
|
2340
2380
|
|
|
2341
2381
|
// src/runtime/clock.ts
|
|
@@ -2345,8 +2385,12 @@ function fixedClock(instant = DEFAULT_NOW) {
|
|
|
2345
2385
|
if (Number.isNaN(ms)) throw new RangeError("invalid clock instant");
|
|
2346
2386
|
return () => new Date(ms);
|
|
2347
2387
|
}
|
|
2388
|
+
function systemClock() {
|
|
2389
|
+
return () => /* @__PURE__ */ new Date();
|
|
2390
|
+
}
|
|
2348
2391
|
function resolveClock(now) {
|
|
2349
2392
|
if (now === void 0) return fixedClock(DEFAULT_NOW);
|
|
2393
|
+
if (now === "system") return systemClock();
|
|
2350
2394
|
if (typeof now === "function") return () => new Date(now().getTime());
|
|
2351
2395
|
return fixedClock(now);
|
|
2352
2396
|
}
|
|
@@ -2411,6 +2455,28 @@ var Prng = class _Prng {
|
|
|
2411
2455
|
return copy;
|
|
2412
2456
|
}
|
|
2413
2457
|
};
|
|
2458
|
+
var OsEntropy = class _OsEntropy extends Prng {
|
|
2459
|
+
constructor() {
|
|
2460
|
+
super(1);
|
|
2461
|
+
}
|
|
2462
|
+
nextU64() {
|
|
2463
|
+
const bytes = new Uint8Array(8);
|
|
2464
|
+
crypto.getRandomValues(bytes);
|
|
2465
|
+
let value = 0n;
|
|
2466
|
+
for (let i = 0; i < 8; i++) {
|
|
2467
|
+
value |= BigInt(bytes[i]) << BigInt(i * 8);
|
|
2468
|
+
}
|
|
2469
|
+
return BigInt.asUintN(64, value);
|
|
2470
|
+
}
|
|
2471
|
+
getState() {
|
|
2472
|
+
return 0n;
|
|
2473
|
+
}
|
|
2474
|
+
setState(_state) {
|
|
2475
|
+
}
|
|
2476
|
+
clone() {
|
|
2477
|
+
return new _OsEntropy();
|
|
2478
|
+
}
|
|
2479
|
+
};
|
|
2414
2480
|
|
|
2415
2481
|
// src/types/value.ts
|
|
2416
2482
|
var SqlReal = class {
|
|
@@ -2517,6 +2583,23 @@ function applyAffinity(value, affinity) {
|
|
|
2517
2583
|
return value;
|
|
2518
2584
|
}
|
|
2519
2585
|
}
|
|
2586
|
+
function applyComparisonAffinity(left, right, leftAffinity, rightAffinity) {
|
|
2587
|
+
const leftNumeric = leftAffinity === "INTEGER" || leftAffinity === "REAL" || leftAffinity === "NUMERIC";
|
|
2588
|
+
const rightNumeric = rightAffinity === "INTEGER" || rightAffinity === "REAL" || rightAffinity === "NUMERIC";
|
|
2589
|
+
const leftNone = leftAffinity === null || leftAffinity === "BLOB";
|
|
2590
|
+
const rightNone = rightAffinity === null || rightAffinity === "BLOB";
|
|
2591
|
+
if (leftNumeric && (rightAffinity === "TEXT" || rightNone)) {
|
|
2592
|
+
const affinity = leftAffinity === "REAL" ? "REAL" : "NUMERIC";
|
|
2593
|
+
return [left, applyAffinity(right, affinity)];
|
|
2594
|
+
}
|
|
2595
|
+
if (rightNumeric && (leftAffinity === "TEXT" || leftNone)) {
|
|
2596
|
+
const affinity = rightAffinity === "REAL" ? "REAL" : "NUMERIC";
|
|
2597
|
+
return [applyAffinity(left, affinity), right];
|
|
2598
|
+
}
|
|
2599
|
+
if (leftAffinity === "TEXT" && rightNone) return [left, applyAffinity(right, "TEXT")];
|
|
2600
|
+
if (rightAffinity === "TEXT" && leftNone) return [applyAffinity(left, "TEXT"), right];
|
|
2601
|
+
return [left, right];
|
|
2602
|
+
}
|
|
2520
2603
|
function coerceToNumber(value) {
|
|
2521
2604
|
if (value === null) return null;
|
|
2522
2605
|
if (value instanceof SqlReal) return value.value;
|
|
@@ -2673,14 +2756,18 @@ function jsonErrorPosition(input) {
|
|
|
2673
2756
|
}
|
|
2674
2757
|
}
|
|
2675
2758
|
function isValidJsonText(input, flags = 0) {
|
|
2676
|
-
const
|
|
2759
|
+
const allowCanonical = flags === 0 || (flags & 1) !== 0;
|
|
2760
|
+
const allowJson5 = (flags & 2) !== 0;
|
|
2677
2761
|
try {
|
|
2678
2762
|
if (allowJson5) {
|
|
2679
2763
|
parseJsonText(input);
|
|
2680
2764
|
return true;
|
|
2681
2765
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2766
|
+
if (allowCanonical) {
|
|
2767
|
+
parseJsonText(input, { strictCanonical: true });
|
|
2768
|
+
return true;
|
|
2769
|
+
}
|
|
2770
|
+
return false;
|
|
2684
2771
|
} catch {
|
|
2685
2772
|
return false;
|
|
2686
2773
|
}
|
|
@@ -4209,7 +4296,11 @@ function applyModifier(date, modifier) {
|
|
|
4209
4296
|
const normalized = modifier.trim().toLowerCase();
|
|
4210
4297
|
if (normalized === "unixepoch") return new Date((date.getTime() / 864e5 + JULIAN_UNIX_EPOCH) * 1e3);
|
|
4211
4298
|
if (normalized === "utc" || normalized === "localtime") return result;
|
|
4212
|
-
|
|
4299
|
+
const weekday = /^weekday\s+([0-6])$/.exec(normalized);
|
|
4300
|
+
if (weekday) {
|
|
4301
|
+
const target = Number(weekday[1]);
|
|
4302
|
+
result.setUTCDate(result.getUTCDate() + (target - result.getUTCDay() + 7) % 7);
|
|
4303
|
+
} else if (normalized === "start of day") result.setUTCHours(0, 0, 0, 0);
|
|
4213
4304
|
else if (normalized === "start of month") {
|
|
4214
4305
|
result.setUTCDate(1);
|
|
4215
4306
|
result.setUTCHours(0, 0, 0, 0);
|
|
@@ -4667,13 +4758,15 @@ var mathFunctions = {
|
|
|
4667
4758
|
function escapeRegexChar(char) {
|
|
4668
4759
|
return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
|
|
4669
4760
|
}
|
|
4670
|
-
function likeLiteral(char) {
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4761
|
+
function likeLiteral(char, caseSensitive) {
|
|
4762
|
+
if (!caseSensitive) {
|
|
4763
|
+
const code = char.codePointAt(0);
|
|
4764
|
+
if (code >= 65 && code <= 90) return `[${char}${char.toLowerCase()}]`;
|
|
4765
|
+
if (code >= 97 && code <= 122) return `[${char}${char.toUpperCase()}]`;
|
|
4766
|
+
}
|
|
4674
4767
|
return escapeRegexChar(char);
|
|
4675
4768
|
}
|
|
4676
|
-
function likeMatch(text2, pattern, escape = null) {
|
|
4769
|
+
function likeMatch(text2, pattern, escape = null, caseSensitive = false) {
|
|
4677
4770
|
if (escape !== null && [...escape].length !== 1) {
|
|
4678
4771
|
throw new SqliteError("ESCAPE expression must be a single character", "other");
|
|
4679
4772
|
}
|
|
@@ -4683,13 +4776,13 @@ function likeMatch(text2, pattern, escape = null) {
|
|
|
4683
4776
|
const char = chars[i];
|
|
4684
4777
|
if (escape !== null && char === escape) {
|
|
4685
4778
|
const next = chars[++i];
|
|
4686
|
-
source += next === void 0 ? likeLiteral(char) : likeLiteral(next);
|
|
4779
|
+
source += next === void 0 ? likeLiteral(char, caseSensitive) : likeLiteral(next, caseSensitive);
|
|
4687
4780
|
} else if (char === "%") {
|
|
4688
4781
|
source += "[\\s\\S]*";
|
|
4689
4782
|
} else if (char === "_") {
|
|
4690
4783
|
source += "[\\s\\S]";
|
|
4691
4784
|
} else {
|
|
4692
|
-
source += likeLiteral(char);
|
|
4785
|
+
source += likeLiteral(char, caseSensitive);
|
|
4693
4786
|
}
|
|
4694
4787
|
}
|
|
4695
4788
|
return new RegExp(`${source}$`).test(text2);
|
|
@@ -4996,11 +5089,11 @@ var scalarFunctions = {
|
|
|
4996
5089
|
requireArgs3("load_extension", args, 1, 2);
|
|
4997
5090
|
throw new SqliteError("not authorized", "misuse");
|
|
4998
5091
|
},
|
|
4999
|
-
like(args) {
|
|
5092
|
+
like(args, context) {
|
|
5000
5093
|
requireArgs3("like", args, 2, 3);
|
|
5001
5094
|
if (args[0] === null || args[1] === null || args[2] === null) return null;
|
|
5002
5095
|
const escape = args[2] === void 0 ? null : text(args[2]);
|
|
5003
|
-
return likeMatch(text(args[1]), text(args[0]), escape) ? 1 : 0;
|
|
5096
|
+
return likeMatch(text(args[1]), text(args[0]), escape, context.caseSensitiveLike === true) ? 1 : 0;
|
|
5004
5097
|
},
|
|
5005
5098
|
glob(args) {
|
|
5006
5099
|
requireArgs3("glob", args, 2);
|
|
@@ -5345,7 +5438,8 @@ function sqlOr(left, right) {
|
|
|
5345
5438
|
if (leftTruth === null || rightTruth === null) return null;
|
|
5346
5439
|
return 0;
|
|
5347
5440
|
}
|
|
5348
|
-
function compareResult(op, left, right, collation) {
|
|
5441
|
+
function compareResult(op, left, right, collation, leftAffinity = null, rightAffinity = null) {
|
|
5442
|
+
[left, right] = applyComparisonAffinity(left, right, leftAffinity, rightAffinity);
|
|
5349
5443
|
if (op === "IS" || op === "IS NOT" || op === "IS DISTINCT FROM" || op === "IS NOT DISTINCT FROM") {
|
|
5350
5444
|
const equal = left === null || right === null ? left === right : (collation ? compareWithCollation(left, right, collation) : compareSql(left, right)) === 0;
|
|
5351
5445
|
if (op === "IS" || op === "IS NOT DISTINCT FROM") return booleanValue(equal);
|
|
@@ -5398,11 +5492,18 @@ function evalBinary(op, leftExpr, rightExpr, ctx) {
|
|
|
5398
5492
|
if (["=", "==", "!=", "<>", "<", "<=", ">", ">=", "IS", "IS NOT", "IS DISTINCT FROM", "IS NOT DISTINCT FROM"].includes(
|
|
5399
5493
|
op
|
|
5400
5494
|
)) {
|
|
5401
|
-
return compareResult(
|
|
5495
|
+
return compareResult(
|
|
5496
|
+
op,
|
|
5497
|
+
left,
|
|
5498
|
+
right,
|
|
5499
|
+
resolveComparisonCollation(leftExpr, rightExpr, ctx) ?? void 0,
|
|
5500
|
+
resolveComparisonAffinity(leftExpr, ctx),
|
|
5501
|
+
resolveComparisonAffinity(rightExpr, ctx)
|
|
5502
|
+
);
|
|
5402
5503
|
}
|
|
5403
5504
|
if (op === "LIKE" || op === "NOT LIKE" || op === "GLOB" || op === "NOT GLOB") {
|
|
5404
5505
|
if (left === null || right === null) return null;
|
|
5405
|
-
const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right)) : globMatch(textValue(left), textValue(right));
|
|
5506
|
+
const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right), null, ctx.functionContext?.caseSensitiveLike === true) : globMatch(textValue(left), textValue(right));
|
|
5406
5507
|
return booleanValue(op.startsWith("NOT") ? !matches : matches);
|
|
5407
5508
|
}
|
|
5408
5509
|
if (left === null || right === null) return null;
|
|
@@ -5577,14 +5678,26 @@ function inheritedCollation(expr, ctx) {
|
|
|
5577
5678
|
return null;
|
|
5578
5679
|
}
|
|
5579
5680
|
}
|
|
5580
|
-
function
|
|
5681
|
+
function resolveComparisonAffinity(expr, ctx) {
|
|
5682
|
+
switch (expr.type) {
|
|
5683
|
+
case "column":
|
|
5684
|
+
return ctx.resolveAffinity?.(expr.table, expr.name) ?? ctx.parent?.resolveAffinity?.(expr.table, expr.name) ?? null;
|
|
5685
|
+
case "cast":
|
|
5686
|
+
return affinityFromTypeName(expr.typeName);
|
|
5687
|
+
case "collate":
|
|
5688
|
+
return resolveComparisonAffinity(expr.expr, ctx);
|
|
5689
|
+
default:
|
|
5690
|
+
return null;
|
|
5691
|
+
}
|
|
5692
|
+
}
|
|
5693
|
+
function evalIn(left, values, not, leftAffinity) {
|
|
5581
5694
|
if (values.length === 0) return booleanValue(not);
|
|
5582
5695
|
if (left === null) return null;
|
|
5583
5696
|
let sawNull = false;
|
|
5584
5697
|
for (const value of values) {
|
|
5585
5698
|
if (value === null) {
|
|
5586
5699
|
sawNull = true;
|
|
5587
|
-
} else if (compareSql(left, value) === 0) {
|
|
5700
|
+
} else if (compareSql(...applyComparisonAffinity(left, value, leftAffinity, null)) === 0) {
|
|
5588
5701
|
return booleanValue(!not);
|
|
5589
5702
|
}
|
|
5590
5703
|
}
|
|
@@ -5630,8 +5743,9 @@ function evalExpr(expr, ctx) {
|
|
|
5630
5743
|
case "between": {
|
|
5631
5744
|
const value = evalExpr(expr.expr, ctx);
|
|
5632
5745
|
const collation = resolveComparisonCollation(expr.expr, expr.lower, ctx) ?? resolveComparisonCollation(expr.expr, expr.upper, ctx) ?? void 0;
|
|
5633
|
-
const
|
|
5634
|
-
const
|
|
5746
|
+
const affinity = resolveComparisonAffinity(expr.expr, ctx);
|
|
5747
|
+
const lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation, affinity);
|
|
5748
|
+
const result = sqlAnd(lower, () => compareResult("<=", value, evalExpr(expr.upper, ctx), collation, affinity));
|
|
5635
5749
|
if (result === null) return null;
|
|
5636
5750
|
return expr.not ? booleanValue(result === 0) : result;
|
|
5637
5751
|
}
|
|
@@ -5658,14 +5772,19 @@ function evalExpr(expr, ctx) {
|
|
|
5658
5772
|
}
|
|
5659
5773
|
const left = evalExpr(expr.expr, ctx);
|
|
5660
5774
|
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);
|
|
5775
|
+
return evalIn(left, values, expr.not, resolveComparisonAffinity(expr.expr, ctx));
|
|
5662
5776
|
}
|
|
5663
5777
|
case "like": {
|
|
5664
5778
|
const value = evalExpr(expr.expr, ctx);
|
|
5665
5779
|
const pattern = evalExpr(expr.pattern, ctx);
|
|
5666
5780
|
const escape = expr.escape === null ? null : evalExpr(expr.escape, ctx);
|
|
5667
5781
|
if (value === null || pattern === null || escape === null && expr.escape !== null) return null;
|
|
5668
|
-
const match = expr.op === "LIKE" ? likeMatch(
|
|
5782
|
+
const match = expr.op === "LIKE" ? likeMatch(
|
|
5783
|
+
textValue(value),
|
|
5784
|
+
textValue(pattern),
|
|
5785
|
+
escape === null ? null : textValue(escape),
|
|
5786
|
+
ctx.functionContext?.caseSensitiveLike === true
|
|
5787
|
+
) : globMatch(textValue(value), textValue(pattern));
|
|
5669
5788
|
return booleanValue(expr.not ? !match : match);
|
|
5670
5789
|
}
|
|
5671
5790
|
case "case": {
|
|
@@ -6806,7 +6925,7 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
6806
6925
|
insert(values, rowid) {
|
|
6807
6926
|
const command = this.detectCommand(values);
|
|
6808
6927
|
if (command !== null) {
|
|
6809
|
-
this.runCommand(command, values);
|
|
6928
|
+
this.runCommand(command, values, rowid);
|
|
6810
6929
|
return 0;
|
|
6811
6930
|
}
|
|
6812
6931
|
const assigned = rowid ?? this.nextRowid++;
|
|
@@ -6955,18 +7074,63 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
6955
7074
|
}
|
|
6956
7075
|
return parts.join(" ");
|
|
6957
7076
|
}
|
|
6958
|
-
/** FTS3 matchinfo
|
|
6959
|
-
matchinfo(cursor,
|
|
7077
|
+
/** FTS3 matchinfo format encoded as native-style 32-bit little-endian integers. */
|
|
7078
|
+
matchinfo(cursor, format = "pcx") {
|
|
6960
7079
|
const row = this.rows.get(cursor.rowid);
|
|
6961
7080
|
const nPhrase = Math.max(1, cursor.phraseTerms.length);
|
|
6962
7081
|
const nCol = this.columns.length;
|
|
6963
|
-
const values = [
|
|
6964
|
-
for (
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
7082
|
+
const values = [];
|
|
7083
|
+
for (const request of format) {
|
|
7084
|
+
switch (request) {
|
|
7085
|
+
case "p":
|
|
7086
|
+
values.push(nPhrase);
|
|
7087
|
+
break;
|
|
7088
|
+
case "c":
|
|
7089
|
+
values.push(nCol);
|
|
7090
|
+
break;
|
|
7091
|
+
case "s":
|
|
7092
|
+
for (let c = 0; c < nCol; c++) {
|
|
7093
|
+
let longest = 0;
|
|
7094
|
+
let run = 0;
|
|
7095
|
+
for (let p = 0; p < nPhrase; p++) {
|
|
7096
|
+
const phrase = cursor.phraseTerms[p] ?? [];
|
|
7097
|
+
if (row && this.phraseFreq(row, this.columns[c], phrase) > 0) {
|
|
7098
|
+
run++;
|
|
7099
|
+
longest = Math.max(longest, run);
|
|
7100
|
+
} else {
|
|
7101
|
+
run = 0;
|
|
7102
|
+
}
|
|
7103
|
+
}
|
|
7104
|
+
values.push(longest);
|
|
7105
|
+
}
|
|
7106
|
+
break;
|
|
7107
|
+
case "x":
|
|
7108
|
+
for (let p = 0; p < nPhrase; p++) {
|
|
7109
|
+
const phrase = cursor.phraseTerms[p] ?? [];
|
|
7110
|
+
for (let c = 0; c < nCol; c++) {
|
|
7111
|
+
const column = this.columns[c];
|
|
7112
|
+
const localHits = row ? this.phraseFreq(row, column, phrase) : 0;
|
|
7113
|
+
let globalHits = 0;
|
|
7114
|
+
let matchingRows = 0;
|
|
7115
|
+
for (const candidate of this.rows.values()) {
|
|
7116
|
+
const hits = this.phraseFreq(candidate, column, phrase);
|
|
7117
|
+
globalHits += hits;
|
|
7118
|
+
if (hits > 0) matchingRows++;
|
|
7119
|
+
}
|
|
7120
|
+
values.push(localHits, globalHits, matchingRows);
|
|
7121
|
+
}
|
|
7122
|
+
}
|
|
7123
|
+
break;
|
|
7124
|
+
case "y":
|
|
7125
|
+
for (let p = 0; p < nPhrase; p++) {
|
|
7126
|
+
const phrase = cursor.phraseTerms[p] ?? [];
|
|
7127
|
+
for (let c = 0; c < nCol; c++) {
|
|
7128
|
+
values.push(row ? this.phraseFreq(row, this.columns[c], phrase) : 0);
|
|
7129
|
+
}
|
|
7130
|
+
}
|
|
7131
|
+
break;
|
|
7132
|
+
default:
|
|
7133
|
+
throw new SqliteError(`unrecognized matchinfo request: ${request}`, "other");
|
|
6970
7134
|
}
|
|
6971
7135
|
}
|
|
6972
7136
|
const buf = new Uint8Array(values.length * 4);
|
|
@@ -7014,13 +7178,9 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7014
7178
|
if (!values.has(key)) return null;
|
|
7015
7179
|
const v = values.get(key);
|
|
7016
7180
|
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
7181
|
return v;
|
|
7022
7182
|
}
|
|
7023
|
-
runCommand(command, _values) {
|
|
7183
|
+
runCommand(command, _values, rowid) {
|
|
7024
7184
|
const cmd = command.toLowerCase();
|
|
7025
7185
|
if (cmd === "optimize") return;
|
|
7026
7186
|
if (cmd === "rebuild") {
|
|
@@ -7035,13 +7195,15 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7035
7195
|
"other"
|
|
7036
7196
|
);
|
|
7037
7197
|
}
|
|
7038
|
-
for (const
|
|
7198
|
+
for (const rowid2 of [...this.rows.keys()]) this.delete(rowid2);
|
|
7039
7199
|
return;
|
|
7040
7200
|
}
|
|
7041
7201
|
if (cmd.startsWith("merge=") || cmd.startsWith("automerge=")) {
|
|
7042
7202
|
throw new SqliteError("SQL logic error", "other");
|
|
7043
7203
|
}
|
|
7044
7204
|
if (cmd === "delete") {
|
|
7205
|
+
if (rowid === void 0) throw new SqliteError("SQL logic error", "other");
|
|
7206
|
+
this.delete(rowid);
|
|
7045
7207
|
return;
|
|
7046
7208
|
}
|
|
7047
7209
|
throw new SqliteError("SQL logic error", "other");
|
|
@@ -7274,11 +7436,6 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
|
|
|
7274
7436
|
}
|
|
7275
7437
|
return n;
|
|
7276
7438
|
}
|
|
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
7439
|
docLength(row) {
|
|
7283
7440
|
let n = 0;
|
|
7284
7441
|
for (const col of this.indexedColumns()) n += (row.tokensByColumn.get(col.toLowerCase()) ?? []).length;
|
|
@@ -7542,6 +7699,8 @@ var Table = class _Table {
|
|
|
7542
7699
|
scanCache = null;
|
|
7543
7700
|
/** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
|
|
7544
7701
|
equalityHashes = null;
|
|
7702
|
+
/** Cached maximum rowid. `undefined` means recompute after deleting the maximum. */
|
|
7703
|
+
maximumRowid = null;
|
|
7545
7704
|
frozen = false;
|
|
7546
7705
|
constructor(name, columns, options = {}) {
|
|
7547
7706
|
this.name = name;
|
|
@@ -7744,7 +7903,12 @@ var Table = class _Table {
|
|
|
7744
7903
|
if (alias) values.set(normalizeColumnName(alias.name), targetKey);
|
|
7745
7904
|
const candidate = { rowid: targetKey, values };
|
|
7746
7905
|
this.validate(candidate, key);
|
|
7747
|
-
if (targetKey !== key)
|
|
7906
|
+
if (targetKey !== key) {
|
|
7907
|
+
this.rows.delete(key);
|
|
7908
|
+
if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
|
|
7909
|
+
this.maximumRowid = void 0;
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
7748
7912
|
this.rows.set(targetKey, candidate);
|
|
7749
7913
|
this.advanceNextRowid(targetKey);
|
|
7750
7914
|
this.reindexEquality(existing, candidate);
|
|
@@ -7758,6 +7922,9 @@ var Table = class _Table {
|
|
|
7758
7922
|
if (this.withoutRowid) this.clusteredRows.delete(this.makeClusterKey(existing.values));
|
|
7759
7923
|
this.unindexEquality(existing);
|
|
7760
7924
|
this.invalidateScan();
|
|
7925
|
+
if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
|
|
7926
|
+
this.maximumRowid = void 0;
|
|
7927
|
+
}
|
|
7761
7928
|
return this.rows.delete(key);
|
|
7762
7929
|
}
|
|
7763
7930
|
*scan() {
|
|
@@ -7779,6 +7946,7 @@ var Table = class _Table {
|
|
|
7779
7946
|
strict: this.strict
|
|
7780
7947
|
});
|
|
7781
7948
|
copy.nextRowid = this.nextRowid;
|
|
7949
|
+
copy.maximumRowid = this.maximumRowid;
|
|
7782
7950
|
for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
|
|
7783
7951
|
for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
|
|
7784
7952
|
return copy;
|
|
@@ -7791,6 +7959,7 @@ var Table = class _Table {
|
|
|
7791
7959
|
}
|
|
7792
7960
|
/** Rebuild clustered storage after snapshot decode or bulk load. */
|
|
7793
7961
|
rebuildClusteredRows() {
|
|
7962
|
+
this.maximumRowid = void 0;
|
|
7794
7963
|
if (!this.withoutRowid) return;
|
|
7795
7964
|
this.clusteredRows.clear();
|
|
7796
7965
|
for (const row of this.rows.values()) {
|
|
@@ -7899,11 +8068,23 @@ var Table = class _Table {
|
|
|
7899
8068
|
return primary[0];
|
|
7900
8069
|
}
|
|
7901
8070
|
allocateRowid() {
|
|
8071
|
+
if (!this.columns.some((column) => column.autoincrement)) {
|
|
8072
|
+
if (this.maximumRowid === void 0) {
|
|
8073
|
+
this.maximumRowid = null;
|
|
8074
|
+
for (const rowid of this.rows.keys()) {
|
|
8075
|
+
if (this.maximumRowid === null || compareRowids(rowid, this.maximumRowid) > 0) this.maximumRowid = rowid;
|
|
8076
|
+
}
|
|
8077
|
+
}
|
|
8078
|
+
return this.maximumRowid === null ? 1 : incrementRowid(this.maximumRowid);
|
|
8079
|
+
}
|
|
7902
8080
|
let candidate = canonicalRowid(this.nextRowid);
|
|
7903
8081
|
while (this.rows.has(candidate)) candidate = incrementRowid(candidate);
|
|
7904
8082
|
return candidate;
|
|
7905
8083
|
}
|
|
7906
8084
|
advanceNextRowid(rowid) {
|
|
8085
|
+
if (this.maximumRowid === null || this.maximumRowid !== void 0 && compareRowids(rowid, this.maximumRowid) > 0) {
|
|
8086
|
+
this.maximumRowid = rowid;
|
|
8087
|
+
}
|
|
7907
8088
|
if (compareRowids(rowid, this.nextRowid) >= 0) this.nextRowid = incrementRowid(rowid);
|
|
7908
8089
|
}
|
|
7909
8090
|
};
|
|
@@ -7993,6 +8174,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
7993
8174
|
changes = 0;
|
|
7994
8175
|
totalChanges = 0;
|
|
7995
8176
|
foreignKeysEnabled = false;
|
|
8177
|
+
/** When true, LIKE / like() are case-sensitive (SQLite `PRAGMA case_sensitive_like`). */
|
|
8178
|
+
caseSensitiveLike = false;
|
|
7996
8179
|
schemaVersion = 0;
|
|
7997
8180
|
userVersion = 0;
|
|
7998
8181
|
databaseForSchema(schema, qualifiedForError) {
|
|
@@ -8437,6 +8620,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8437
8620
|
copy.changes = this.changes;
|
|
8438
8621
|
copy.totalChanges = this.totalChanges;
|
|
8439
8622
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8623
|
+
copy.caseSensitiveLike = this.caseSensitiveLike;
|
|
8440
8624
|
copy.schemaVersion = this.schemaVersion;
|
|
8441
8625
|
copy.userVersion = this.userVersion;
|
|
8442
8626
|
return copy;
|
|
@@ -8456,6 +8640,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8456
8640
|
copy.changes = this.changes;
|
|
8457
8641
|
copy.totalChanges = this.totalChanges;
|
|
8458
8642
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8643
|
+
copy.caseSensitiveLike = this.caseSensitiveLike;
|
|
8459
8644
|
copy.schemaVersion = this.schemaVersion;
|
|
8460
8645
|
copy.userVersion = this.userVersion;
|
|
8461
8646
|
return copy;
|
|
@@ -8517,6 +8702,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8517
8702
|
this.changes = copy.changes;
|
|
8518
8703
|
this.totalChanges = copy.totalChanges;
|
|
8519
8704
|
this.foreignKeysEnabled = copy.foreignKeysEnabled;
|
|
8705
|
+
this.caseSensitiveLike = copy.caseSensitiveLike;
|
|
8520
8706
|
this.schemaVersion = copy.schemaVersion;
|
|
8521
8707
|
this.userVersion = copy.userVersion;
|
|
8522
8708
|
}
|
|
@@ -8983,6 +9169,8 @@ var ExecutionEnv = class {
|
|
|
8983
9169
|
maxRows = Number.POSITIVE_INFINITY;
|
|
8984
9170
|
includeNamedRows = true;
|
|
8985
9171
|
includeValues = true;
|
|
9172
|
+
/** Source text of the statement currently executing (for sqlite_master.sql). */
|
|
9173
|
+
statementSql = null;
|
|
8986
9174
|
constructor(state, transactions, params = [], functions = defaultFunctionRegistry, hooks = {}) {
|
|
8987
9175
|
this.state = state;
|
|
8988
9176
|
this.transactions = transactions;
|
|
@@ -9001,6 +9189,7 @@ var ExecutionEnv = class {
|
|
|
9001
9189
|
this.maxRows = Number.POSITIVE_INFINITY;
|
|
9002
9190
|
this.includeNamedRows = true;
|
|
9003
9191
|
this.includeValues = true;
|
|
9192
|
+
this.statementSql = null;
|
|
9004
9193
|
}
|
|
9005
9194
|
getBoundParameter(name) {
|
|
9006
9195
|
if (typeof name === "number") {
|
|
@@ -9019,8 +9208,9 @@ var ExecutionEnv = class {
|
|
|
9019
9208
|
createEvalContext(row = null, parent) {
|
|
9020
9209
|
const scope = row ?? this.triggerScope;
|
|
9021
9210
|
const cells = scope?.cells ?? [];
|
|
9211
|
+
const inheritedParent = parent ?? (row !== null && this.triggerScope !== null && row !== this.triggerScope ? this.createEvalContext(this.triggerScope) : void 0);
|
|
9022
9212
|
const context = {
|
|
9023
|
-
parent,
|
|
9213
|
+
parent: inheritedParent,
|
|
9024
9214
|
ftsMatch: scope?.ftsMatch ?? null,
|
|
9025
9215
|
functions: this.functions,
|
|
9026
9216
|
functionContext: {
|
|
@@ -9030,6 +9220,7 @@ var ExecutionEnv = class {
|
|
|
9030
9220
|
now: this.hooks.now,
|
|
9031
9221
|
random: this.hooks.random,
|
|
9032
9222
|
randomU64: this.hooks.randomU64,
|
|
9223
|
+
caseSensitiveLike: this.state.caseSensitiveLike,
|
|
9033
9224
|
ftsMatch: scope?.ftsMatch ?? null,
|
|
9034
9225
|
ftsRowid: scope?.rowid,
|
|
9035
9226
|
ftsSourceTable: scope?.sourceTable,
|
|
@@ -9088,6 +9279,17 @@ var ExecutionEnv = class {
|
|
|
9088
9279
|
const cell = matches[0];
|
|
9089
9280
|
return cell.affinity === "REAL" && typeof cell.value === "number" ? "real" : storageClassOf(cell.value);
|
|
9090
9281
|
},
|
|
9282
|
+
resolveAffinity: (table, name) => {
|
|
9283
|
+
const key = name.toLowerCase();
|
|
9284
|
+
const tableKey = table?.toLowerCase();
|
|
9285
|
+
const matches = cells.filter((cell) => {
|
|
9286
|
+
if ((cell.nameLower ?? cell.name.toLowerCase()) !== key) return false;
|
|
9287
|
+
if (table === null) return !cell.hiddenByUsing;
|
|
9288
|
+
return (cell.tableLower ?? cell.table?.toLowerCase()) === tableKey;
|
|
9289
|
+
});
|
|
9290
|
+
if (matches.length !== 1) return null;
|
|
9291
|
+
return matches[0].affinity ?? null;
|
|
9292
|
+
},
|
|
9091
9293
|
resolveCollation: (table, name) => {
|
|
9092
9294
|
const key = name.toLowerCase();
|
|
9093
9295
|
const tableKey = table?.toLowerCase();
|
|
@@ -9220,6 +9422,78 @@ function executeDetach(stmt, env) {
|
|
|
9220
9422
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
9221
9423
|
}
|
|
9222
9424
|
|
|
9425
|
+
// src/schema/master-sql.ts
|
|
9426
|
+
function normalizeMasterSql(sql) {
|
|
9427
|
+
const trimmed = sql.trim().replace(/;+\s*$/u, "").trim();
|
|
9428
|
+
if (!trimmed) return trimmed;
|
|
9429
|
+
const tokens = tokenize(trimmed);
|
|
9430
|
+
if (tokens.length === 0 || tokens[0].kind === "EOF") return trimmed;
|
|
9431
|
+
let i = 0;
|
|
9432
|
+
const at = (offset, ...kinds) => {
|
|
9433
|
+
const tok = tokens[i + offset];
|
|
9434
|
+
return tok !== void 0 && kinds.includes(tok.kind);
|
|
9435
|
+
};
|
|
9436
|
+
if (!at(0, "CREATE")) return trimmed;
|
|
9437
|
+
i++;
|
|
9438
|
+
if (at(0, "TEMP", "TEMPORARY")) i++;
|
|
9439
|
+
let headKind = "table";
|
|
9440
|
+
if (at(0, "UNIQUE") && at(1, "INDEX")) {
|
|
9441
|
+
headKind = "unique_index";
|
|
9442
|
+
i += 2;
|
|
9443
|
+
} else if (at(0, "VIRTUAL") && at(1, "TABLE")) {
|
|
9444
|
+
headKind = "vtable";
|
|
9445
|
+
i += 2;
|
|
9446
|
+
} else if (at(0, "TABLE")) {
|
|
9447
|
+
headKind = "table";
|
|
9448
|
+
i++;
|
|
9449
|
+
} else if (at(0, "VIEW")) {
|
|
9450
|
+
headKind = "view";
|
|
9451
|
+
i++;
|
|
9452
|
+
} else if (at(0, "INDEX")) {
|
|
9453
|
+
headKind = "index";
|
|
9454
|
+
i++;
|
|
9455
|
+
} else if (at(0, "TRIGGER")) {
|
|
9456
|
+
headKind = "trigger";
|
|
9457
|
+
i++;
|
|
9458
|
+
} else {
|
|
9459
|
+
return trimmed;
|
|
9460
|
+
}
|
|
9461
|
+
if (at(0, "IF") && at(1, "NOT") && at(2, "EXISTS")) i += 3;
|
|
9462
|
+
let nameText = "";
|
|
9463
|
+
let nameEnd = 0;
|
|
9464
|
+
if (tokens[i] && tokens[i].kind !== "EOF" && at(1, "DOT") && tokens[i + 2] && tokens[i + 2].kind !== "EOF") {
|
|
9465
|
+
nameText = trimmed.slice(tokens[i + 2].start, tokens[i + 2].end);
|
|
9466
|
+
nameEnd = tokens[i + 2].end;
|
|
9467
|
+
i += 3;
|
|
9468
|
+
} else if (tokens[i] && tokens[i].kind !== "EOF") {
|
|
9469
|
+
nameText = trimmed.slice(tokens[i].start, tokens[i].end);
|
|
9470
|
+
nameEnd = tokens[i].end;
|
|
9471
|
+
i++;
|
|
9472
|
+
}
|
|
9473
|
+
const body = nameEnd < trimmed.length ? trimmed.slice(nameEnd) : "";
|
|
9474
|
+
const needsSpace = body.length > 0 && !/^[\s(]/u.test(body);
|
|
9475
|
+
const head = headKind === "unique_index" ? "CREATE UNIQUE INDEX" : headKind === "vtable" ? "CREATE VIRTUAL TABLE" : headKind === "view" ? "CREATE VIEW" : headKind === "index" ? "CREATE INDEX" : headKind === "trigger" ? "CREATE TRIGGER" : "CREATE TABLE";
|
|
9476
|
+
if (body.length === 0) return `${head} ${nameText}`;
|
|
9477
|
+
return needsSpace ? `${head} ${nameText} ${body}` : `${head} ${nameText}${body}`;
|
|
9478
|
+
}
|
|
9479
|
+
function appendAddColumnToMasterSql(originalSql, alterSql) {
|
|
9480
|
+
if (!originalSql || !alterSql) return originalSql;
|
|
9481
|
+
const match = alterSql.match(/\bADD\s+(?:COLUMN\s+)?(.+)$/iu);
|
|
9482
|
+
if (!match?.[1]) return originalSql;
|
|
9483
|
+
const colDef = match[1].trim().replace(/;+\s*$/u, "").trim();
|
|
9484
|
+
const close = originalSql.lastIndexOf(")");
|
|
9485
|
+
if (close < 0) return originalSql;
|
|
9486
|
+
return `${originalSql.slice(0, close)}, ${colDef}${originalSql.slice(close)}`;
|
|
9487
|
+
}
|
|
9488
|
+
function synthesizeCtasMasterSql(tableName, columnNames) {
|
|
9489
|
+
const cols = columnNames.map(quoteIdentIfNeeded).join(",");
|
|
9490
|
+
return `CREATE TABLE ${quoteIdentIfNeeded(tableName)}(${cols})`;
|
|
9491
|
+
}
|
|
9492
|
+
function quoteIdentIfNeeded(name) {
|
|
9493
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)) return name;
|
|
9494
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
9495
|
+
}
|
|
9496
|
+
|
|
9223
9497
|
// src/functions/window.ts
|
|
9224
9498
|
function rowsEqual(left, right) {
|
|
9225
9499
|
if (left.length !== right.length) return false;
|
|
@@ -9495,6 +9769,8 @@ function queryPragma(name, args, env) {
|
|
|
9495
9769
|
switch (key) {
|
|
9496
9770
|
case "foreign_keys":
|
|
9497
9771
|
return single("foreign_keys", env.state.foreignKeysEnabled ? 1 : 0);
|
|
9772
|
+
case "case_sensitive_like":
|
|
9773
|
+
return single("case_sensitive_like", env.state.caseSensitiveLike ? 1 : 0);
|
|
9498
9774
|
case "user_version":
|
|
9499
9775
|
return single("user_version", env.state.userVersion);
|
|
9500
9776
|
case "schema_version":
|
|
@@ -9922,83 +10198,6 @@ function hasTableValuedFunction(name) {
|
|
|
9922
10198
|
return hasRegisteredTableValuedFunction(name);
|
|
9923
10199
|
}
|
|
9924
10200
|
|
|
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
10201
|
// src/expressions/equals.ts
|
|
10003
10202
|
function exprEquals(left, right) {
|
|
10004
10203
|
if (left.type !== right.type) return false;
|
|
@@ -10392,6 +10591,83 @@ function columnOnTable(column, alias, tableName) {
|
|
|
10392
10591
|
return column.table !== null && matchesTable(column.table, alias, tableName);
|
|
10393
10592
|
}
|
|
10394
10593
|
|
|
10594
|
+
// src/schema/catalog.ts
|
|
10595
|
+
function schemaCatalogRows(state) {
|
|
10596
|
+
const tables = [...state.tables.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10597
|
+
const indexes = [...state.indexes.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10598
|
+
const views = [...state.views.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10599
|
+
const triggers = [...state.triggers.values()].sort((a, b) => compareNames2(a.name, b.name));
|
|
10600
|
+
const rows = [];
|
|
10601
|
+
let rootpage = 2;
|
|
10602
|
+
for (const table of tables) {
|
|
10603
|
+
rows.push({
|
|
10604
|
+
type: "table",
|
|
10605
|
+
name: table.name,
|
|
10606
|
+
tbl_name: table.name,
|
|
10607
|
+
rootpage: rootpage++,
|
|
10608
|
+
sql: table.originalSql
|
|
10609
|
+
});
|
|
10610
|
+
}
|
|
10611
|
+
for (const index of indexes) {
|
|
10612
|
+
rows.push({
|
|
10613
|
+
type: "index",
|
|
10614
|
+
name: index.name,
|
|
10615
|
+
tbl_name: index.tableName,
|
|
10616
|
+
rootpage: rootpage++,
|
|
10617
|
+
sql: index.originalSql
|
|
10618
|
+
});
|
|
10619
|
+
}
|
|
10620
|
+
for (const view of views) {
|
|
10621
|
+
rows.push({
|
|
10622
|
+
type: "view",
|
|
10623
|
+
name: view.name,
|
|
10624
|
+
tbl_name: view.name,
|
|
10625
|
+
rootpage: 0,
|
|
10626
|
+
sql: view.originalSql
|
|
10627
|
+
});
|
|
10628
|
+
}
|
|
10629
|
+
for (const trigger of triggers) {
|
|
10630
|
+
rows.push({
|
|
10631
|
+
type: "trigger",
|
|
10632
|
+
name: trigger.name,
|
|
10633
|
+
tbl_name: trigger.tableName,
|
|
10634
|
+
rootpage: 0,
|
|
10635
|
+
sql: trigger.originalSql
|
|
10636
|
+
});
|
|
10637
|
+
}
|
|
10638
|
+
return rows;
|
|
10639
|
+
}
|
|
10640
|
+
function compareNames2(a, b) {
|
|
10641
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
10642
|
+
}
|
|
10643
|
+
function buildSchemaCatalog(state, name = "sqlite_schema") {
|
|
10644
|
+
const catalog = new Table(name, [
|
|
10645
|
+
makeColumnInfo("type", "TEXT"),
|
|
10646
|
+
makeColumnInfo("name", "TEXT"),
|
|
10647
|
+
makeColumnInfo("tbl_name", "TEXT"),
|
|
10648
|
+
makeColumnInfo("rootpage", "INTEGER"),
|
|
10649
|
+
makeColumnInfo("sql", "TEXT")
|
|
10650
|
+
]);
|
|
10651
|
+
for (const row of schemaCatalogRows(state)) {
|
|
10652
|
+
catalog.insert({
|
|
10653
|
+
values: {
|
|
10654
|
+
type: row.type,
|
|
10655
|
+
name: row.name,
|
|
10656
|
+
tbl_name: row.tbl_name,
|
|
10657
|
+
rootpage: row.rootpage,
|
|
10658
|
+
sql: row.sql
|
|
10659
|
+
}
|
|
10660
|
+
});
|
|
10661
|
+
}
|
|
10662
|
+
return catalog;
|
|
10663
|
+
}
|
|
10664
|
+
function buildSqliteSchema(state) {
|
|
10665
|
+
return buildSchemaCatalog(state, "sqlite_schema");
|
|
10666
|
+
}
|
|
10667
|
+
function buildSqliteMaster(state) {
|
|
10668
|
+
return buildSchemaCatalog(state, "sqlite_master");
|
|
10669
|
+
}
|
|
10670
|
+
|
|
10395
10671
|
// src/executor/simple-select.ts
|
|
10396
10672
|
function tryExecuteSimpleSelect(stmt, env) {
|
|
10397
10673
|
if (stmt.with || stmt.compound || stmt.distinct || stmt.groupBy.length > 0 || stmt.having || stmt.windows.length > 0)
|
|
@@ -10440,7 +10716,9 @@ function tryExecuteSimpleSelect(stmt, env) {
|
|
|
10440
10716
|
} catch {
|
|
10441
10717
|
return null;
|
|
10442
10718
|
}
|
|
10443
|
-
|
|
10719
|
+
const column = eq.column.toLowerCase();
|
|
10720
|
+
const affinity = isRowidName2(column) ? "INTEGER" : table.columns.find((item) => (item.nameLower ?? item.name.toLowerCase()) === column)?.affinity;
|
|
10721
|
+
filters.push({ column, value: affinity ? applyAffinity(value, affinity) : value });
|
|
10444
10722
|
}
|
|
10445
10723
|
}
|
|
10446
10724
|
let limit = env.maxRows;
|
|
@@ -10622,7 +10900,7 @@ function executeSelect2(stmt, env, parent) {
|
|
|
10622
10900
|
}
|
|
10623
10901
|
const savedCtes = new Map(env.ctes);
|
|
10624
10902
|
try {
|
|
10625
|
-
if (stmt.with) executeWith(stmt, env, parent);
|
|
10903
|
+
if (stmt.with) executeWith(stmt.with, env, parent);
|
|
10626
10904
|
const base = executeSelectCore(
|
|
10627
10905
|
{
|
|
10628
10906
|
...stmt,
|
|
@@ -10678,22 +10956,39 @@ function executeSelect2(stmt, env, parent) {
|
|
|
10678
10956
|
for (const [name, result] of savedCtes) env.ctes.set(name, result);
|
|
10679
10957
|
}
|
|
10680
10958
|
}
|
|
10681
|
-
function executeWith(
|
|
10682
|
-
for (const cte of
|
|
10959
|
+
function executeWith(withClause, env, parent) {
|
|
10960
|
+
for (const cte of withClause.ctes) {
|
|
10683
10961
|
const key = cte.name.toLowerCase();
|
|
10684
|
-
if (
|
|
10962
|
+
if (withClause.recursive && referencesTable(cte.select, cte.name) && cte.select.compound) {
|
|
10685
10963
|
const anchor = executeSelect2({ ...cte.select, compound: null }, env, parent);
|
|
10686
10964
|
const columns = cte.columns ?? anchor.columns;
|
|
10687
|
-
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10965
|
+
const recursive = cte.select.compound.select;
|
|
10966
|
+
const limit = recursive.limit ? toInteger(evalExpr(recursive.limit.limit, env.createEvalContext(null, parent))) : null;
|
|
10967
|
+
if (recursive.limit && limit === null) throw new SqliteError("datatype mismatch", "datatype_mismatch");
|
|
10968
|
+
const maxRows = limit === null || Number(limit) < 0 ? Number.POSITIVE_INFINITY : Number(limit);
|
|
10969
|
+
const queue = resultValues(anchor);
|
|
10970
|
+
const discovered = [...queue];
|
|
10971
|
+
const accumulated = [];
|
|
10972
|
+
if (recursive.orderBy.length > 0) {
|
|
10973
|
+
queue.sort((left, right) => compareCteQueueRows(left, right, columns, recursive.orderBy, env, parent));
|
|
10974
|
+
}
|
|
10975
|
+
while (queue.length > 0 && accumulated.length < maxRows) {
|
|
10976
|
+
const current = queue.shift();
|
|
10977
|
+
accumulated.push(current);
|
|
10978
|
+
env.ctes.set(key, valuesToResult(columns, [current]));
|
|
10979
|
+
const nextResult = executeSelect2({ ...recursive, orderBy: [], limit: null }, env, parent);
|
|
10692
10980
|
const candidates = resultValues(nextResult);
|
|
10693
|
-
const additions =
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10981
|
+
const additions = [];
|
|
10982
|
+
for (const candidate of candidates) {
|
|
10983
|
+
if (cte.select.compound.op !== "UNION ALL" && [...discovered, ...additions].some((existing) => rowsEqual2(existing, candidate)))
|
|
10984
|
+
continue;
|
|
10985
|
+
additions.push(candidate);
|
|
10986
|
+
}
|
|
10987
|
+
discovered.push(...additions);
|
|
10988
|
+
queue.push(...additions);
|
|
10989
|
+
if (recursive.orderBy.length > 0) {
|
|
10990
|
+
queue.sort((left, right) => compareCteQueueRows(left, right, columns, recursive.orderBy, env, parent));
|
|
10991
|
+
}
|
|
10697
10992
|
}
|
|
10698
10993
|
env.ctes.set(key, valuesToResult(columns, accumulated));
|
|
10699
10994
|
} else {
|
|
@@ -10703,6 +10998,26 @@ function executeWith(stmt, env, parent) {
|
|
|
10703
10998
|
}
|
|
10704
10999
|
}
|
|
10705
11000
|
}
|
|
11001
|
+
function compareCteQueueRows(left, right, columns, order, env, parent) {
|
|
11002
|
+
const scope = (values) => ({
|
|
11003
|
+
cells: columns.map((name, index) => ({ table: null, name, value: values[index] ?? null }))
|
|
11004
|
+
});
|
|
11005
|
+
const leftScope = scope(left);
|
|
11006
|
+
const rightScope = scope(right);
|
|
11007
|
+
for (const item of order) {
|
|
11008
|
+
if (item.expr.type === "literal" && typeof item.expr.value === "number" && Number.isInteger(item.expr.value)) {
|
|
11009
|
+
const index = item.expr.value - 1;
|
|
11010
|
+
const result2 = compareNullable(left[index] ?? null, right[index] ?? null, item);
|
|
11011
|
+
if (result2 !== 0) return result2;
|
|
11012
|
+
continue;
|
|
11013
|
+
}
|
|
11014
|
+
const a = evalExpr(item.expr, env.createEvalContext(leftScope, parent));
|
|
11015
|
+
const b = evalExpr(item.expr, env.createEvalContext(rightScope, parent));
|
|
11016
|
+
const result = compareNullable(a, b, item, leftScope);
|
|
11017
|
+
if (result !== 0) return result;
|
|
11018
|
+
}
|
|
11019
|
+
return 0;
|
|
11020
|
+
}
|
|
10706
11021
|
function executeSelectCore(stmt, env, parent) {
|
|
10707
11022
|
let scopes;
|
|
10708
11023
|
let skipWhere = false;
|
|
@@ -10758,10 +11073,18 @@ function executeSelectCore(stmt, env, parent) {
|
|
|
10758
11073
|
}
|
|
10759
11074
|
}
|
|
10760
11075
|
const groupBy = stmt.groupBy.map((expr) => {
|
|
10761
|
-
if (expr.type
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
11076
|
+
if (expr.type === "literal" && typeof expr.value === "number" && Number.isInteger(expr.value)) {
|
|
11077
|
+
const column = stmt.columns[expr.value - 1];
|
|
11078
|
+
if (column?.type !== "expr") throw new SqliteError(`${expr.value}th GROUP BY term out of range`, "other");
|
|
11079
|
+
return column.expr;
|
|
11080
|
+
}
|
|
11081
|
+
if (expr.type === "column" && expr.table === null) {
|
|
11082
|
+
const column = stmt.columns.find(
|
|
11083
|
+
(candidate) => candidate.type === "expr" && candidate.alias?.toLowerCase() === expr.name.toLowerCase()
|
|
11084
|
+
);
|
|
11085
|
+
if (column?.type === "expr") return column.expr;
|
|
11086
|
+
}
|
|
11087
|
+
return expr;
|
|
10765
11088
|
});
|
|
10766
11089
|
const groups = aggregate ? groupRows(scopes, groupBy, env, parent) : scopes.map((scope) => [scope]);
|
|
10767
11090
|
const windowScopes = aggregate ? groups.map((group) => group[0] ?? { cells: [] }) : scopes;
|
|
@@ -11218,7 +11541,10 @@ function groupRows(rows, expressions, env, parent) {
|
|
|
11218
11541
|
const groups = [];
|
|
11219
11542
|
const indexByKey = /* @__PURE__ */ new Map();
|
|
11220
11543
|
for (const row of rows) {
|
|
11221
|
-
const keyValues = expressions.map((expr) =>
|
|
11544
|
+
const keyValues = expressions.map((expr) => {
|
|
11545
|
+
const value = evalExpr(expr, env.createEvalContext(row, parent));
|
|
11546
|
+
return expr.type === "collate" ? normalizeForCollation(value, expr.collation) : value;
|
|
11547
|
+
});
|
|
11222
11548
|
const key = valueKey(keyValues);
|
|
11223
11549
|
const index = indexByKey.get(key);
|
|
11224
11550
|
if (index === void 0) {
|
|
@@ -11241,7 +11567,8 @@ function aggregateValue(expr, rows, env, parent) {
|
|
|
11241
11567
|
const accumulator = env.functions.createAggregate(expr.name);
|
|
11242
11568
|
if (!accumulator) throw new SqliteError(`no such aggregate function: ${expr.name}`, "other");
|
|
11243
11569
|
const seen = [];
|
|
11244
|
-
|
|
11570
|
+
const orderedRows = expr.orderBy.length > 0 ? [...rows].sort((a, b) => compareScopes(a, b, expr.orderBy, env, parent)) : rows;
|
|
11571
|
+
for (const row of orderedRows) {
|
|
11245
11572
|
const ctx = env.createEvalContext(row, parent);
|
|
11246
11573
|
if (expr.filter && isTruthySql(evalExpr(expr.filter, ctx)) !== true) continue;
|
|
11247
11574
|
const args = expr.args === "*" ? [] : expr.args.map((arg) => evalExpr(arg, ctx));
|
|
@@ -11357,8 +11684,10 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
|
11357
11684
|
if (!spec.frame) return [0, spec.orderBy.length > 0 ? defaultFrameEnd : Math.max(0, length - 1)];
|
|
11358
11685
|
const isRangeLike = spec.frame.type === "RANGE" || spec.frame.type === "GROUPS";
|
|
11359
11686
|
let peerFirst = index;
|
|
11687
|
+
let peerLast = index;
|
|
11360
11688
|
if (isRangeLike && spec.orderBy.length > 0) {
|
|
11361
11689
|
while (peerFirst > 0 && rowsEqual2(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
|
|
11690
|
+
while (peerLast + 1 < length && rowsEqual2(orderKeys[peerLast], orderKeys[peerLast + 1])) peerLast++;
|
|
11362
11691
|
}
|
|
11363
11692
|
const bound = (item, isStart) => {
|
|
11364
11693
|
switch (item.kind) {
|
|
@@ -11367,12 +11696,54 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
|
11367
11696
|
case "unbounded_following":
|
|
11368
11697
|
return Math.max(0, length - 1);
|
|
11369
11698
|
case "current_row":
|
|
11370
|
-
if (isRangeLike) return isStart ? peerFirst :
|
|
11699
|
+
if (isRangeLike) return isStart ? peerFirst : peerLast;
|
|
11371
11700
|
return index;
|
|
11372
11701
|
case "preceding":
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11702
|
+
case "following": {
|
|
11703
|
+
const offset = Number(evalExpr(item.expr, ctx));
|
|
11704
|
+
if (spec.frame?.type === "GROUPS") {
|
|
11705
|
+
const groups = [];
|
|
11706
|
+
for (let i = 0; i < length; ) {
|
|
11707
|
+
let last = i;
|
|
11708
|
+
while (last + 1 < length && rowsEqual2(orderKeys[last], orderKeys[last + 1])) last++;
|
|
11709
|
+
groups.push({ first: i, last });
|
|
11710
|
+
i = last + 1;
|
|
11711
|
+
}
|
|
11712
|
+
const currentGroup = groups.findIndex((group) => index >= group.first && index <= group.last);
|
|
11713
|
+
const delta = item.kind === "preceding" ? -offset : offset;
|
|
11714
|
+
const target = groups[Math.max(0, Math.min(groups.length - 1, currentGroup + delta))];
|
|
11715
|
+
return isStart ? target.first : target.last;
|
|
11716
|
+
}
|
|
11717
|
+
if (spec.frame?.type === "RANGE" && spec.orderBy.length === 1) {
|
|
11718
|
+
const rawCurrent = orderKeys[index]?.[0];
|
|
11719
|
+
const current = typeof rawCurrent === "number" ? rawCurrent : typeof rawCurrent === "bigint" ? Number(rawCurrent) : rawCurrent && typeof rawCurrent === "object" && "value" in rawCurrent ? Number(rawCurrent.value) : Number.NaN;
|
|
11720
|
+
if (!Number.isFinite(current) || !Number.isFinite(offset)) return isStart ? peerFirst : peerLast;
|
|
11721
|
+
const descending = spec.orderBy[0]?.dir === "DESC";
|
|
11722
|
+
const signed = item.kind === "preceding" ? -offset : offset;
|
|
11723
|
+
const target = current + (descending ? -signed : signed);
|
|
11724
|
+
const numericKey = (position) => {
|
|
11725
|
+
const value = orderKeys[position]?.[0];
|
|
11726
|
+
if (typeof value === "number") return value;
|
|
11727
|
+
if (typeof value === "bigint") return Number(value);
|
|
11728
|
+
if (value && typeof value === "object" && "value" in value) return Number(value.value);
|
|
11729
|
+
return Number.NaN;
|
|
11730
|
+
};
|
|
11731
|
+
if (isStart) {
|
|
11732
|
+
for (let i = 0; i < length; i++) {
|
|
11733
|
+
const value = numericKey(i);
|
|
11734
|
+
if (descending && value <= target || !descending && value >= target) return i;
|
|
11735
|
+
}
|
|
11736
|
+
return length;
|
|
11737
|
+
}
|
|
11738
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
11739
|
+
const value = numericKey(i);
|
|
11740
|
+
if (descending && value >= target || !descending && value <= target) return i;
|
|
11741
|
+
}
|
|
11742
|
+
return -1;
|
|
11743
|
+
}
|
|
11744
|
+
const rowOffset = Number(toInteger(evalExpr(item.expr, ctx)) ?? 0);
|
|
11745
|
+
return item.kind === "preceding" ? Math.max(0, index - rowOffset) : Math.min(Math.max(0, length - 1), index + rowOffset);
|
|
11746
|
+
}
|
|
11376
11747
|
}
|
|
11377
11748
|
};
|
|
11378
11749
|
return [bound(spec.frame.start, true), bound(spec.frame.end, false)];
|
|
@@ -11704,14 +12075,17 @@ function scanFtsVocab(db, alias, vocab) {
|
|
|
11704
12075
|
|
|
11705
12076
|
// src/executor/ddl.ts
|
|
11706
12077
|
function executeCreateTable(stmt, env) {
|
|
12078
|
+
const sourceSql = env.statementSql ? normalizeMasterSql(env.statementSql) : null;
|
|
11707
12079
|
if (!stmt.asSelect) {
|
|
11708
|
-
env.state.createTable(stmt);
|
|
12080
|
+
env.state.createTable(stmt, sourceSql);
|
|
11709
12081
|
env.state.recordChange(0);
|
|
11710
12082
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11711
12083
|
}
|
|
11712
12084
|
const result = executeSelect2(stmt.asSelect, env);
|
|
11713
12085
|
const columns = result.columns.map((name) => ({ name, typeName: null, constraints: [] }));
|
|
11714
|
-
const
|
|
12086
|
+
const bareName = stmt.name.includes(".") ? stmt.name.split(".").pop() ?? stmt.name : stmt.name;
|
|
12087
|
+
const ctasSql = synthesizeCtasMasterSql(bareName, result.columns);
|
|
12088
|
+
const table = env.state.createTable({ ...stmt, columns, constraints: [], asSelect: null }, ctasSql);
|
|
11715
12089
|
for (const values of resultValues(result)) {
|
|
11716
12090
|
table.insert(
|
|
11717
12091
|
new Map(table.columns.map((column, index) => [normalizeColumnName(column.name), values[index] ?? null]))
|
|
@@ -11736,7 +12110,7 @@ function executeCreateTable(stmt, env) {
|
|
|
11736
12110
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11737
12111
|
}
|
|
11738
12112
|
function executeCreateIndex(stmt, env) {
|
|
11739
|
-
const index = env.state.createIndex(stmt);
|
|
12113
|
+
const index = env.state.createIndex(stmt, env.statementSql ? normalizeMasterSql(env.statementSql) : null);
|
|
11740
12114
|
const table = env.state.getTable(stmt.table);
|
|
11741
12115
|
try {
|
|
11742
12116
|
for (const row of table.scan()) {
|
|
@@ -11816,6 +12190,7 @@ function executeAlterTable(stmt, env) {
|
|
|
11816
12190
|
throw new SqliteError("Cannot add a NOT NULL column with default value NULL", "other");
|
|
11817
12191
|
table.columns.push(column);
|
|
11818
12192
|
for (const row of table.rows.values()) row.values.set(normalizeColumnName(column.name), defaultValue);
|
|
12193
|
+
table.originalSql = appendAddColumnToMasterSql(table.originalSql, env.statementSql);
|
|
11819
12194
|
table.clearEqualityHashes();
|
|
11820
12195
|
env.state.schemaVersion++;
|
|
11821
12196
|
} else {
|
|
@@ -11863,7 +12238,7 @@ function executeDropIndex(stmt, env) {
|
|
|
11863
12238
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11864
12239
|
}
|
|
11865
12240
|
function executeCreateView(stmt, env) {
|
|
11866
|
-
env.state.createView(stmt);
|
|
12241
|
+
env.state.createView(stmt, env.statementSql ? normalizeMasterSql(env.statementSql) : null);
|
|
11867
12242
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11868
12243
|
}
|
|
11869
12244
|
function executeDropView(stmt, env) {
|
|
@@ -11956,7 +12331,7 @@ function executeCreateTrigger(stmt, env) {
|
|
|
11956
12331
|
forEachRow: stmt.forEachRow,
|
|
11957
12332
|
body: stmt.body,
|
|
11958
12333
|
updateColumns: stmt.updateColumns,
|
|
11959
|
-
originalSql: null
|
|
12334
|
+
originalSql: env.statementSql ? normalizeMasterSql(env.statementSql) : null
|
|
11960
12335
|
});
|
|
11961
12336
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11962
12337
|
}
|
|
@@ -11999,6 +12374,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
|
|
|
11999
12374
|
}
|
|
12000
12375
|
env.triggerDepth++;
|
|
12001
12376
|
const savedScope = env.triggerScope;
|
|
12377
|
+
const savedLastInsertRowid = env.state.lastInsertRowid;
|
|
12002
12378
|
env.triggerScope = triggerScope(table, oldRow, newValues);
|
|
12003
12379
|
try {
|
|
12004
12380
|
for (const statement of trigger.body) {
|
|
@@ -12016,6 +12392,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
|
|
|
12016
12392
|
}
|
|
12017
12393
|
throw error;
|
|
12018
12394
|
} finally {
|
|
12395
|
+
env.state.lastInsertRowid = savedLastInsertRowid;
|
|
12019
12396
|
env.triggerScope = savedScope;
|
|
12020
12397
|
env.triggerDepth--;
|
|
12021
12398
|
}
|
|
@@ -12051,7 +12428,7 @@ function triggerScope(table, oldRow, newValues) {
|
|
|
12051
12428
|
|
|
12052
12429
|
// src/executor/vtable.ts
|
|
12053
12430
|
function executeCreateVirtualTable(stmt, env) {
|
|
12054
|
-
env.state.createVirtualTable(stmt);
|
|
12431
|
+
env.state.createVirtualTable(stmt, env.statementSql ? normalizeMasterSql(env.statementSql) : null);
|
|
12055
12432
|
env.state.recordChange(0);
|
|
12056
12433
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
12057
12434
|
}
|
|
@@ -12110,9 +12487,20 @@ function storeColumnValue(table, column, value) {
|
|
|
12110
12487
|
return applyAffinity(value, column.affinity);
|
|
12111
12488
|
}
|
|
12112
12489
|
function executeInsert(stmt, env) {
|
|
12490
|
+
try {
|
|
12491
|
+
return withDmlCtes(stmt.with, env, () => executeInsertCore(stmt, env));
|
|
12492
|
+
} catch (error) {
|
|
12493
|
+
handleConflictRollback(stmt.mode === "insert_or_rollback", error, env);
|
|
12494
|
+
throw error;
|
|
12495
|
+
}
|
|
12496
|
+
}
|
|
12497
|
+
function executeInsertCore(stmt, env) {
|
|
12113
12498
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12114
12499
|
return executeVirtualInsert(stmt, env);
|
|
12115
12500
|
}
|
|
12501
|
+
const totalBefore = env.state.totalChanges;
|
|
12502
|
+
const view = writableView(stmt.table, "INSERT", env);
|
|
12503
|
+
if (view) return executeViewInsert(stmt, view, env, totalBefore);
|
|
12116
12504
|
const fast = tryFastInsert(stmt, env);
|
|
12117
12505
|
if (fast) return fast;
|
|
12118
12506
|
const table = env.state.getWritableTable(stmt.table);
|
|
@@ -12130,11 +12518,14 @@ function executeInsert(stmt, env) {
|
|
|
12130
12518
|
const suppliedIndexes = table.columns.map(
|
|
12131
12519
|
(column) => columnNames.findIndex((name) => name.toLowerCase() === (column.nameLower ?? column.name.toLowerCase()))
|
|
12132
12520
|
);
|
|
12133
|
-
const unconstrained = table.isUnconstrained() && !stmt.upsert && stmt.mode === "insert" && stmt.returning.length === 0 && rowidIndexes.length === 0;
|
|
12521
|
+
const unconstrained = table.isUnconstrained() && env.state.databaseForTable(table).triggers.size === 0 && !stmt.upsert && stmt.mode === "insert" && stmt.returning.length === 0 && rowidIndexes.length === 0;
|
|
12134
12522
|
if (unconstrained) {
|
|
12135
12523
|
for (const source of sourceRows) {
|
|
12136
12524
|
if (source.length !== columnNames.length)
|
|
12137
|
-
throw new SqliteError(
|
|
12525
|
+
throw new SqliteError(
|
|
12526
|
+
stmt.columns ? `${source.length} values for ${columnNames.length} columns` : `table ${table.name} has ${columnNames.length} columns but ${source.length} values were supplied`,
|
|
12527
|
+
"other"
|
|
12528
|
+
);
|
|
12138
12529
|
const values = /* @__PURE__ */ new Map();
|
|
12139
12530
|
for (let index = 0; index < table.columns.length; index++) {
|
|
12140
12531
|
const column = table.columns[index];
|
|
@@ -12150,7 +12541,10 @@ function executeInsert(stmt, env) {
|
|
|
12150
12541
|
}
|
|
12151
12542
|
for (const source of sourceRows) {
|
|
12152
12543
|
if (source.length !== columnNames.length)
|
|
12153
|
-
throw new SqliteError(
|
|
12544
|
+
throw new SqliteError(
|
|
12545
|
+
stmt.columns ? `${source.length} values for ${columnNames.length} columns` : `table ${table.name} has ${columnNames.length} columns but ${source.length} values were supplied`,
|
|
12546
|
+
"other"
|
|
12547
|
+
);
|
|
12154
12548
|
const values = /* @__PURE__ */ new Map();
|
|
12155
12549
|
for (const column of table.columns) {
|
|
12156
12550
|
if (column.generated) continue;
|
|
@@ -12238,9 +12632,12 @@ function executeInsert(stmt, env) {
|
|
|
12238
12632
|
validateRow(table, row, env);
|
|
12239
12633
|
if (table.indexes.length > 0) addIndexes(table, row, env);
|
|
12240
12634
|
if (env.state.foreignKeysEnabled) checkForeignKeys(table, row, env);
|
|
12635
|
+
if (!table.withoutRowid) {
|
|
12636
|
+
last = rowid;
|
|
12637
|
+
env.state.lastInsertRowid = rowid;
|
|
12638
|
+
}
|
|
12241
12639
|
fireInsertTriggers("AFTER", table, row.values, null, env);
|
|
12242
12640
|
changes++;
|
|
12243
|
-
last = table.withoutRowid ? 0 : rowid;
|
|
12244
12641
|
if (stmt.returning.length)
|
|
12245
12642
|
returningRows.push(projectReturning(stmt.returning, scopeFor(table, row, stmt.table, env), env));
|
|
12246
12643
|
} catch (error) {
|
|
@@ -12253,12 +12650,12 @@ function executeInsert(stmt, env) {
|
|
|
12253
12650
|
throw error;
|
|
12254
12651
|
}
|
|
12255
12652
|
}
|
|
12256
|
-
|
|
12257
|
-
if (stmt.returning.length === 0) return emptyResult(
|
|
12258
|
-
return valuesToResult(returningNames(stmt.returning, table), returningRows,
|
|
12653
|
+
const reportedChanges = finalizeDmlChanges(totalBefore, changes, env, last);
|
|
12654
|
+
if (stmt.returning.length === 0) return emptyResult(reportedChanges, last);
|
|
12655
|
+
return valuesToResult(returningNames(stmt.returning, table), returningRows, reportedChanges, last);
|
|
12259
12656
|
}
|
|
12260
12657
|
function evaluateInsertSource(stmt, env) {
|
|
12261
|
-
if (stmt.select) return resultValues(executeSelect2(
|
|
12658
|
+
if (stmt.select) return resultValues(executeSelect2(stmt.select, env));
|
|
12262
12659
|
if (!stmt.values) return [[]];
|
|
12263
12660
|
const ctx = env.createEvalContext();
|
|
12264
12661
|
return stmt.values.map(
|
|
@@ -12287,6 +12684,7 @@ function tryFastInsert(stmt, env) {
|
|
|
12287
12684
|
} catch {
|
|
12288
12685
|
return null;
|
|
12289
12686
|
}
|
|
12687
|
+
if (env.state.databaseForTable(table).triggers.size > 0) return null;
|
|
12290
12688
|
if (!table.isUnconstrained()) return null;
|
|
12291
12689
|
const values = /* @__PURE__ */ new Map();
|
|
12292
12690
|
for (const slot of plan.slots) values.set(slot.key, applyAffinity(slot.read(env), slot.affinity));
|
|
@@ -12331,9 +12729,20 @@ function buildFastInsertPlan(stmt, env) {
|
|
|
12331
12729
|
return { tableName: stmt.table, slots };
|
|
12332
12730
|
}
|
|
12333
12731
|
function executeUpdate(stmt, env) {
|
|
12732
|
+
try {
|
|
12733
|
+
return withDmlCtes(stmt.with, env, () => executeUpdateCore(stmt, env));
|
|
12734
|
+
} catch (error) {
|
|
12735
|
+
handleConflictRollback(stmt.or === "rollback", error, env);
|
|
12736
|
+
throw error;
|
|
12737
|
+
}
|
|
12738
|
+
}
|
|
12739
|
+
function executeUpdateCore(stmt, env) {
|
|
12334
12740
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12335
12741
|
return executeVirtualUpdate(stmt, env);
|
|
12336
12742
|
}
|
|
12743
|
+
const totalBefore = env.state.totalChanges;
|
|
12744
|
+
const view = writableView(stmt.table, "UPDATE", env);
|
|
12745
|
+
if (view) return executeViewUpdate(stmt, view, env, totalBefore);
|
|
12337
12746
|
const table = env.state.getWritableTable(stmt.table);
|
|
12338
12747
|
const alias = stmt.alias ?? stmt.table;
|
|
12339
12748
|
const candidates = [];
|
|
@@ -12386,13 +12795,24 @@ function executeUpdate(stmt, env) {
|
|
|
12386
12795
|
throw error;
|
|
12387
12796
|
}
|
|
12388
12797
|
}
|
|
12389
|
-
|
|
12390
|
-
return valuesToResult(
|
|
12798
|
+
const reportedChanges = finalizeDmlChanges(totalBefore, changes, env);
|
|
12799
|
+
return valuesToResult(
|
|
12800
|
+
returningNames(stmt.returning, table),
|
|
12801
|
+
returningRows,
|
|
12802
|
+
reportedChanges,
|
|
12803
|
+
env.state.lastInsertRowid
|
|
12804
|
+
);
|
|
12391
12805
|
}
|
|
12392
12806
|
function executeDelete(stmt, env) {
|
|
12807
|
+
return withDmlCtes(stmt.with, env, () => executeDeleteCore(stmt, env));
|
|
12808
|
+
}
|
|
12809
|
+
function executeDeleteCore(stmt, env) {
|
|
12393
12810
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
12394
12811
|
return executeVirtualDelete(stmt, env);
|
|
12395
12812
|
}
|
|
12813
|
+
const totalBefore = env.state.totalChanges;
|
|
12814
|
+
const view = writableView(stmt.table, "DELETE", env);
|
|
12815
|
+
if (view) return executeViewDelete(stmt, view, env, totalBefore);
|
|
12396
12816
|
const table = env.state.getWritableTable(stmt.table);
|
|
12397
12817
|
const selectedSource = stmt.where === null ? null : tryIndexedTableRows({ type: "table", schema: null, name: stmt.table, alias: stmt.alias }, stmt.where, env);
|
|
12398
12818
|
const selected = [...selectedSource ? selectedSource.rows : table.scan()].filter((row) => {
|
|
@@ -12410,8 +12830,120 @@ function executeDelete(stmt, env) {
|
|
|
12410
12830
|
fireDeleteTriggers("AFTER", table, row, env);
|
|
12411
12831
|
changes++;
|
|
12412
12832
|
}
|
|
12413
|
-
|
|
12414
|
-
return valuesToResult(
|
|
12833
|
+
const reportedChanges = finalizeDmlChanges(totalBefore, changes, env);
|
|
12834
|
+
return valuesToResult(
|
|
12835
|
+
returningNames(stmt.returning, table),
|
|
12836
|
+
returningRows,
|
|
12837
|
+
reportedChanges,
|
|
12838
|
+
env.state.lastInsertRowid
|
|
12839
|
+
);
|
|
12840
|
+
}
|
|
12841
|
+
function withDmlCtes(withClause, env, execute) {
|
|
12842
|
+
if (!withClause) return execute();
|
|
12843
|
+
const savedCtes = new Map(env.ctes);
|
|
12844
|
+
try {
|
|
12845
|
+
executeWith(withClause, env);
|
|
12846
|
+
return execute();
|
|
12847
|
+
} finally {
|
|
12848
|
+
env.ctes.clear();
|
|
12849
|
+
for (const [name, result] of savedCtes) env.ctes.set(name, result);
|
|
12850
|
+
}
|
|
12851
|
+
}
|
|
12852
|
+
function handleConflictRollback(rollback, error, env) {
|
|
12853
|
+
if (rollback && env.transactions.inTransaction && error instanceof SqliteError && error.category.startsWith("constraint")) {
|
|
12854
|
+
env.transactions.rollback();
|
|
12855
|
+
}
|
|
12856
|
+
}
|
|
12857
|
+
function writableView(name, event, env) {
|
|
12858
|
+
const { schema, bare } = splitQualifiedName(name);
|
|
12859
|
+
const db = env.state.databaseForSchema(schema, name);
|
|
12860
|
+
const view = db.views.get(bare.toLowerCase());
|
|
12861
|
+
if (!view) return null;
|
|
12862
|
+
const hasInsteadOf = [...db.triggers.values()].some(
|
|
12863
|
+
(trigger) => trigger.tableName.toLowerCase() === bare.toLowerCase() && trigger.event === event && trigger.timing === "INSTEAD"
|
|
12864
|
+
);
|
|
12865
|
+
if (!hasInsteadOf) {
|
|
12866
|
+
env.state.getWritableTable(name);
|
|
12867
|
+
throw new SqliteError(`cannot modify ${bare} because it is a view`, "other");
|
|
12868
|
+
}
|
|
12869
|
+
const names = view.columns ?? executeSelect2(view.select, env).columns;
|
|
12870
|
+
return {
|
|
12871
|
+
schema,
|
|
12872
|
+
name: bare,
|
|
12873
|
+
view,
|
|
12874
|
+
table: new Table(
|
|
12875
|
+
bare,
|
|
12876
|
+
names.map((column) => makeColumnInfo(column, null))
|
|
12877
|
+
)
|
|
12878
|
+
};
|
|
12879
|
+
}
|
|
12880
|
+
function executeViewInsert(stmt, target, env, totalBefore) {
|
|
12881
|
+
const columnNames = stmt.columns ?? target.table.columns.map((column) => column.name);
|
|
12882
|
+
for (const name of columnNames) columnOf(target.table, name);
|
|
12883
|
+
const suppliedIndexes = target.table.columns.map(
|
|
12884
|
+
(column) => columnNames.findIndex((name) => name.toLowerCase() === normalizeColumnName(column.name))
|
|
12885
|
+
);
|
|
12886
|
+
for (const source of evaluateInsertSource(stmt, env)) {
|
|
12887
|
+
if (source.length !== columnNames.length) {
|
|
12888
|
+
throw new SqliteError(`${source.length} values for ${columnNames.length} columns`, "other");
|
|
12889
|
+
}
|
|
12890
|
+
const values = /* @__PURE__ */ new Map();
|
|
12891
|
+
target.table.columns.forEach((column, index) => {
|
|
12892
|
+
const supplied = suppliedIndexes[index] ?? -1;
|
|
12893
|
+
values.set(normalizeColumnName(column.name), supplied < 0 ? null : source[supplied] ?? null);
|
|
12894
|
+
});
|
|
12895
|
+
fireInsertTriggers("INSTEAD", target.table, values, null, env);
|
|
12896
|
+
}
|
|
12897
|
+
const changes = finalizeDmlChanges(totalBefore, 0, env);
|
|
12898
|
+
return emptyResult(changes, env.state.lastInsertRowid);
|
|
12899
|
+
}
|
|
12900
|
+
function executeViewUpdate(stmt, target, env, totalBefore) {
|
|
12901
|
+
const alias = stmt.alias ?? target.name;
|
|
12902
|
+
const scopes = scanView(target, alias, env);
|
|
12903
|
+
const updatedColumns = new Set(
|
|
12904
|
+
stmt.set.flatMap((item) => item.columns.map((name) => columnOf(target.table, name).name))
|
|
12905
|
+
);
|
|
12906
|
+
for (const scope of scopes) {
|
|
12907
|
+
const ctx = env.createEvalContext(scope);
|
|
12908
|
+
if (stmt.where && isTruthySql(evalExpr(stmt.where, ctx)) !== true) continue;
|
|
12909
|
+
const oldRow = viewRow(target.table, scope);
|
|
12910
|
+
const updates = evaluateSet(stmt.set, target.table, ctx);
|
|
12911
|
+
const newValues = mergedValues(target.table, oldRow, updates);
|
|
12912
|
+
fireUpdateTriggers("INSTEAD", target.table, oldRow, newValues, updatedColumns, env);
|
|
12913
|
+
}
|
|
12914
|
+
const changes = finalizeDmlChanges(totalBefore, 0, env);
|
|
12915
|
+
return emptyResult(changes, env.state.lastInsertRowid);
|
|
12916
|
+
}
|
|
12917
|
+
function executeViewDelete(stmt, target, env, totalBefore) {
|
|
12918
|
+
const alias = stmt.alias ?? target.name;
|
|
12919
|
+
for (const scope of scanView(target, alias, env)) {
|
|
12920
|
+
if (stmt.where && isTruthySql(evalExpr(stmt.where, env.createEvalContext(scope))) !== true) continue;
|
|
12921
|
+
fireDeleteTriggers("INSTEAD", target.table, viewRow(target.table, scope), env);
|
|
12922
|
+
}
|
|
12923
|
+
const changes = finalizeDmlChanges(totalBefore, 0, env);
|
|
12924
|
+
return emptyResult(changes, env.state.lastInsertRowid);
|
|
12925
|
+
}
|
|
12926
|
+
function scanView(target, alias, env) {
|
|
12927
|
+
return scanFrom(
|
|
12928
|
+
{ type: "table", schema: target.schema, name: target.name, alias: alias === target.name ? null : alias },
|
|
12929
|
+
env
|
|
12930
|
+
);
|
|
12931
|
+
}
|
|
12932
|
+
function viewRow(table, scope) {
|
|
12933
|
+
const values = /* @__PURE__ */ new Map();
|
|
12934
|
+
for (const column of table.columns) {
|
|
12935
|
+
const key = normalizeColumnName(column.name);
|
|
12936
|
+
const cell = scope.cells.find((candidate) => normalizeColumnName(candidate.name) === key);
|
|
12937
|
+
values.set(key, cell?.value ?? null);
|
|
12938
|
+
}
|
|
12939
|
+
return { rowid: scope.rowid ?? 0, values };
|
|
12940
|
+
}
|
|
12941
|
+
function finalizeDmlChanges(totalBefore, directChanges, env, last) {
|
|
12942
|
+
const triggerChanges = env.state.totalChanges - totalBefore;
|
|
12943
|
+
env.state.recordChange(directChanges, last);
|
|
12944
|
+
const reportedChanges = triggerChanges + directChanges;
|
|
12945
|
+
env.state.changes = reportedChanges;
|
|
12946
|
+
return reportedChanges;
|
|
12415
12947
|
}
|
|
12416
12948
|
function mergedValues(table, row, updates) {
|
|
12417
12949
|
const values = /* @__PURE__ */ new Map();
|
|
@@ -12722,7 +13254,7 @@ function applyReferentialDelete(parent, row, env) {
|
|
|
12722
13254
|
} else if (constraint.onDelete === "SET DEFAULT") {
|
|
12723
13255
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
12724
13256
|
changes += 1 + updated.cascaded;
|
|
12725
|
-
} else if (!fkIsDeferred(constraint, env)) {
|
|
13257
|
+
} else if (constraint.onDelete === "RESTRICT" || !fkIsDeferred(constraint, env)) {
|
|
12726
13258
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12727
13259
|
}
|
|
12728
13260
|
}
|
|
@@ -12765,7 +13297,7 @@ function applyReferentialUpdate(parent, before, after, env) {
|
|
|
12765
13297
|
} else if (constraint.onUpdate === "SET DEFAULT") {
|
|
12766
13298
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
12767
13299
|
changes += 1 + updated.cascaded;
|
|
12768
|
-
} else if (!fkIsDeferred(constraint, env)) {
|
|
13300
|
+
} else if (constraint.onUpdate === "RESTRICT" || !fkIsDeferred(constraint, env)) {
|
|
12769
13301
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12770
13302
|
}
|
|
12771
13303
|
}
|
|
@@ -13046,6 +13578,11 @@ function executePragma(name, expr, env) {
|
|
|
13046
13578
|
if (!env.transactions.inTransaction) env.state.foreignKeysEnabled = coercePragmaTruthy(value);
|
|
13047
13579
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
13048
13580
|
}
|
|
13581
|
+
if (key === "case_sensitive_like" && expr !== null) {
|
|
13582
|
+
const value = evalPragmaSetValue(expr, env);
|
|
13583
|
+
env.state.caseSensitiveLike = coercePragmaTruthy(value);
|
|
13584
|
+
return emptyResult(0, env.state.lastInsertRowid);
|
|
13585
|
+
}
|
|
13049
13586
|
if ((key === "user_version" || key === "schema_version") && expr !== null) {
|
|
13050
13587
|
const value = evalPragmaSetValue(expr, env);
|
|
13051
13588
|
const num2 = coercePragmaInt(value);
|
|
@@ -13193,10 +13730,11 @@ function executeAnalyze(stmt, env) {
|
|
|
13193
13730
|
|
|
13194
13731
|
// src/api/statement.ts
|
|
13195
13732
|
var Statement = class _Statement {
|
|
13196
|
-
constructor(database, sql, statements) {
|
|
13733
|
+
constructor(database, sql, statements, statementSqls) {
|
|
13197
13734
|
this.database = database;
|
|
13198
13735
|
this.sql = sql;
|
|
13199
13736
|
this.statements = statements;
|
|
13737
|
+
this.statementSqls = statementSqls;
|
|
13200
13738
|
this.schemaVersion = database.state.schemaVersion;
|
|
13201
13739
|
}
|
|
13202
13740
|
database;
|
|
@@ -13204,13 +13742,24 @@ var Statement = class _Statement {
|
|
|
13204
13742
|
namedPlan = null;
|
|
13205
13743
|
env = null;
|
|
13206
13744
|
statements;
|
|
13745
|
+
statementSqls;
|
|
13207
13746
|
schemaVersion;
|
|
13208
13747
|
/**
|
|
13209
13748
|
* Construct a {@link Statement} for {@link Database.prepare} / {@link Database.exec}.
|
|
13210
13749
|
* @internal
|
|
13211
13750
|
*/
|
|
13212
|
-
static create(database, sql, statements) {
|
|
13213
|
-
return new _Statement(database, sql, statements);
|
|
13751
|
+
static create(database, sql, statements, statementSqls) {
|
|
13752
|
+
return new _Statement(database, sql, statements, statementSqls ?? statements.map(() => sql));
|
|
13753
|
+
}
|
|
13754
|
+
/** @internal Build from {@link parseUnits}. */
|
|
13755
|
+
static createFromSql(database, sql) {
|
|
13756
|
+
const units = parseUnits(sql);
|
|
13757
|
+
return new _Statement(
|
|
13758
|
+
database,
|
|
13759
|
+
sql,
|
|
13760
|
+
units.map((u) => u.statement),
|
|
13761
|
+
units.map((u) => u.sql)
|
|
13762
|
+
);
|
|
13214
13763
|
}
|
|
13215
13764
|
/**
|
|
13216
13765
|
* Execute for side effects (INSERT / UPDATE / DELETE / DDL).
|
|
@@ -13258,7 +13807,10 @@ var Statement = class _Statement {
|
|
|
13258
13807
|
execute(params, options) {
|
|
13259
13808
|
this.database.assertOpen();
|
|
13260
13809
|
this.reprepareIfSchemaChanged();
|
|
13261
|
-
if (this.statements.length === 0)
|
|
13810
|
+
if (this.statements.length === 0) {
|
|
13811
|
+
if (options?.named) throw new SqliteError("empty statement", "misuse");
|
|
13812
|
+
return emptyResult(this.database.state.changes, this.database.state.lastInsertRowid);
|
|
13813
|
+
}
|
|
13262
13814
|
this.namedPlan ??= planNamedParameters(this.sql);
|
|
13263
13815
|
const expected = this.namedPlan.expectedCount;
|
|
13264
13816
|
if (params.length > 0 && params.length !== expected) {
|
|
@@ -13270,9 +13822,11 @@ var Statement = class _Statement {
|
|
|
13270
13822
|
env.includeValues = true;
|
|
13271
13823
|
this.bindNamed(env, params);
|
|
13272
13824
|
let result;
|
|
13273
|
-
for (
|
|
13274
|
-
|
|
13825
|
+
for (let i = 0; i < this.statements.length; i++) {
|
|
13826
|
+
env.statementSql = this.statementSqls[i] ?? this.sql;
|
|
13827
|
+
result = executeStatement(this.statements[i], env);
|
|
13275
13828
|
}
|
|
13829
|
+
env.statementSql = null;
|
|
13276
13830
|
return result;
|
|
13277
13831
|
}
|
|
13278
13832
|
obtainEnv(params) {
|
|
@@ -13292,7 +13846,9 @@ var Statement = class _Statement {
|
|
|
13292
13846
|
}
|
|
13293
13847
|
reprepareIfSchemaChanged() {
|
|
13294
13848
|
if (this.schemaVersion === this.database.state.schemaVersion) return;
|
|
13295
|
-
|
|
13849
|
+
const units = parseUnits(this.sql);
|
|
13850
|
+
this.statements = units.map((u) => u.statement);
|
|
13851
|
+
this.statementSqls = units.map((u) => u.sql);
|
|
13296
13852
|
this.env = null;
|
|
13297
13853
|
this.namedPlan = null;
|
|
13298
13854
|
this.schemaVersion = this.database.state.schemaVersion;
|
|
@@ -13330,11 +13886,18 @@ function planNamedParameters(sql) {
|
|
|
13330
13886
|
var Database = class {
|
|
13331
13887
|
/** @internal Engine catalog, tables, and mutation counters. */
|
|
13332
13888
|
state = new DatabaseState();
|
|
13333
|
-
/** Seed used to construct the PRNG. */
|
|
13889
|
+
/** Seed used to construct the PRNG. Ignored when {@link randomMode} is `"os"`. */
|
|
13334
13890
|
seed;
|
|
13891
|
+
/** Entropy mode for `random()` / `randomblob()`. */
|
|
13892
|
+
randomMode;
|
|
13893
|
+
/**
|
|
13894
|
+
* When true, `'now'` follows the wall clock and {@link restore} does not freeze it.
|
|
13895
|
+
* @internal
|
|
13896
|
+
*/
|
|
13897
|
+
systemClock;
|
|
13335
13898
|
/**
|
|
13336
13899
|
* PRNG backing `random()` / `randomblob()` and related builtins.
|
|
13337
|
-
* Prefer passing `seed` to the constructor.
|
|
13900
|
+
* Prefer passing `seed` / `random` to the constructor.
|
|
13338
13901
|
* @internal
|
|
13339
13902
|
*/
|
|
13340
13903
|
prng;
|
|
@@ -13357,7 +13920,9 @@ var Database = class {
|
|
|
13357
13920
|
*/
|
|
13358
13921
|
constructor(options = {}) {
|
|
13359
13922
|
this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
|
|
13360
|
-
this.
|
|
13923
|
+
this.randomMode = options.random ?? "deterministic";
|
|
13924
|
+
this.systemClock = options.now === "system";
|
|
13925
|
+
this.prng = this.randomMode === "os" ? new OsEntropy() : new Prng(this.seed);
|
|
13361
13926
|
this.now = resolveClock(options.now);
|
|
13362
13927
|
this.transactions = new TransactionManager(this.state, this.prng);
|
|
13363
13928
|
}
|
|
@@ -13374,7 +13939,7 @@ var Database = class {
|
|
|
13374
13939
|
if (arguments.length > 1) {
|
|
13375
13940
|
throw new SqliteError("exec() does not accept parameters; use prepare() or query()", "misuse");
|
|
13376
13941
|
}
|
|
13377
|
-
Statement.
|
|
13942
|
+
Statement.createFromSql(this, sql).run();
|
|
13378
13943
|
}
|
|
13379
13944
|
/**
|
|
13380
13945
|
* Execute a single-statement query and return all rows as objects keyed by column name.
|
|
@@ -13471,7 +14036,7 @@ var Database = class {
|
|
|
13471
14036
|
this.state.replaceWith(decoded.state, { adopt: true });
|
|
13472
14037
|
if (decoded.runtime) {
|
|
13473
14038
|
this.prng.setState(decoded.runtime.prngState);
|
|
13474
|
-
this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
14039
|
+
if (!this.systemClock) this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
13475
14040
|
}
|
|
13476
14041
|
}
|
|
13477
14042
|
/**
|
|
@@ -13506,6 +14071,15 @@ var Database = class {
|
|
|
13506
14071
|
this.assertOpen();
|
|
13507
14072
|
return this.state.lastInsertRowid;
|
|
13508
14073
|
}
|
|
14074
|
+
/**
|
|
14075
|
+
* Cumulative rows changed by INSERT / UPDATE / DELETE (SQLite `total_changes()`).
|
|
14076
|
+
*
|
|
14077
|
+
* @throws {SqliteError} If the database is closed.
|
|
14078
|
+
*/
|
|
14079
|
+
get totalChanges() {
|
|
14080
|
+
this.assertOpen();
|
|
14081
|
+
return this.state.totalChanges;
|
|
14082
|
+
}
|
|
13509
14083
|
/**
|
|
13510
14084
|
* Throw if {@link close} has already been called.
|
|
13511
14085
|
* @internal
|
|
@@ -13515,14 +14089,19 @@ var Database = class {
|
|
|
13515
14089
|
if (this.closed) throw new SqliteError("Database is closed", "misuse");
|
|
13516
14090
|
}
|
|
13517
14091
|
prepareSingle(sql) {
|
|
13518
|
-
const
|
|
13519
|
-
if (
|
|
14092
|
+
const units = parseUnits(sql);
|
|
14093
|
+
if (units.length === 0) {
|
|
13520
14094
|
throw new SqliteError("empty statement", "misuse");
|
|
13521
14095
|
}
|
|
13522
|
-
if (
|
|
14096
|
+
if (units.length > 1) {
|
|
13523
14097
|
throw new SqliteError("query()/prepare() accept a single statement only; use exec() for scripts", "misuse");
|
|
13524
14098
|
}
|
|
13525
|
-
return Statement.create(
|
|
14099
|
+
return Statement.create(
|
|
14100
|
+
this,
|
|
14101
|
+
sql,
|
|
14102
|
+
units.map((u) => u.statement),
|
|
14103
|
+
units.map((u) => u.sql)
|
|
14104
|
+
);
|
|
13526
14105
|
}
|
|
13527
14106
|
};
|
|
13528
14107
|
var disposeKey = Symbol.dispose;
|