@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/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
- const stmts = [];
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
- stmts.push(this.parseStatement());
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 stmts;
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.check("WITH", "SELECT")) return this.parseSelectStmt();
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.check("WITH", "INSERT") || this.at("INSERT") || this.at("REPLACE")) {
767
+ if (this.at("INSERT") || this.at("REPLACE")) {
751
768
  return this.parseInsertStmt();
752
769
  }
753
- if (this.check("WITH", "UPDATE") || this.at("UPDATE")) return this.parseUpdateStmt();
754
- if (this.check("WITH", "DELETE") || this.at("DELETE")) return this.parseDeleteStmt();
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
- if (this.match("NOT")) this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
817
- else this.match("MATERIALIZED");
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 parseTokens(tokens) {
2332
- return new Parser(tokens).parseStatements();
2372
+ function parseTokenUnits(tokens, source) {
2373
+ return new Parser(tokens, source).parseUnits();
2333
2374
  }
2334
2375
 
2335
2376
  // src/parser/index.ts
2336
- function parse(sql) {
2337
- const tokens = tokenize(sql);
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 allowJson5 = (flags & 2) !== 0 || (flags & 1) !== 0;
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
- parseJsonText(input, { strictCanonical: true });
2683
- return true;
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
- if (normalized === "start of day") result.setUTCHours(0, 0, 0, 0);
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
- const code = char.codePointAt(0);
4672
- if (code >= 65 && code <= 90) return `[${char}${char.toLowerCase()}]`;
4673
- if (code >= 97 && code <= 122) return `[${char}${char.toUpperCase()}]`;
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(op, left, right, resolveComparisonCollation(leftExpr, rightExpr, ctx) ?? void 0);
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 evalIn(left, values, not) {
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 lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation);
5634
- const result = sqlAnd(lower, () => compareResult("<=", value, evalExpr(expr.upper, ctx), collation));
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(textValue(value), textValue(pattern), escape === null ? null : textValue(escape)) : globMatch(textValue(value), textValue(pattern));
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 default format 'pcx' simplified blob of 32-bit LE ints. */
6959
- matchinfo(cursor, _format = "pcx") {
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 = [nPhrase, nCol];
6964
- for (let p = 0; p < nPhrase; p++) {
6965
- for (let c = 0; c < nCol; c++) {
6966
- const col = this.columns[c];
6967
- const term = cursor.phraseTerms[p]?.[0] ?? "";
6968
- const tf = row ? this.termFreq(row, col, term) : 0;
6969
- values.push(tf);
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 rowid of [...this.rows.keys()]) this.delete(rowid);
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) this.rows.delete(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
- filters.push({ column: eq.column.toLowerCase(), value });
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(stmt, env, parent) {
10682
- for (const cte of stmt.with.ctes) {
10959
+ function executeWith(withClause, env, parent) {
10960
+ for (const cte of withClause.ctes) {
10683
10961
  const key = cte.name.toLowerCase();
10684
- if (stmt.with.recursive && referencesTable(cte.select, cte.name) && cte.select.compound) {
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
- let accumulated = resultValues(anchor);
10688
- let delta = accumulated;
10689
- while (true) {
10690
- env.ctes.set(key, valuesToResult(columns, delta));
10691
- const nextResult = executeSelect2(cte.select.compound.select, env, parent);
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 = cte.select.compound.op === "UNION ALL" ? candidates : candidates.filter((row) => !accumulated.some((existing) => rowsEqual2(existing, row)));
10694
- if (additions.length === 0) break;
10695
- accumulated = [...accumulated, ...additions];
10696
- delta = additions;
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 !== "literal" || typeof expr.value !== "number" || !Number.isInteger(expr.value)) return expr;
10762
- const column = stmt.columns[expr.value - 1];
10763
- if (column?.type !== "expr") throw new SqliteError(`${expr.value}th GROUP BY term out of range`, "other");
10764
- return column.expr;
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) => evalExpr(expr, env.createEvalContext(row, parent)));
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
- for (const row of rows) {
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 : defaultFrameEnd;
11699
+ if (isRangeLike) return isStart ? peerFirst : peerLast;
11371
11700
  return index;
11372
11701
  case "preceding":
11373
- return Math.max(0, index - Number(toInteger(evalExpr(item.expr, ctx)) ?? 0));
11374
- case "following":
11375
- return Math.min(Math.max(0, length - 1), index + Number(toInteger(evalExpr(item.expr, ctx)) ?? 0));
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 table = env.state.createTable({ ...stmt, columns, constraints: [], asSelect: null });
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(`${source.length} values for ${columnNames.length} columns`, "other");
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(`${source.length} values for ${columnNames.length} columns`, "other");
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
- env.state.recordChange(changes, last);
12257
- if (stmt.returning.length === 0) return emptyResult(changes, last);
12258
- return valuesToResult(returningNames(stmt.returning, table), returningRows, changes, last);
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({ ...stmt.select, with: stmt.with ?? stmt.select.with }, env));
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
- env.state.recordChange(changes);
12390
- return valuesToResult(returningNames(stmt.returning, table), returningRows, changes, env.state.lastInsertRowid);
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
- env.state.recordChange(changes);
12414
- return valuesToResult(returningNames(stmt.returning, table), returningRows, changes, env.state.lastInsertRowid);
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) throw new SqliteError("empty statement", "misuse");
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 (const statement of this.statements) {
13274
- result = executeStatement(statement, env);
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
- this.statements = parse(this.sql);
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.prng = new Prng(this.seed);
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.create(this, sql, parse(sql)).run();
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 statements = parse(sql);
13519
- if (statements.length === 0) {
14092
+ const units = parseUnits(sql);
14093
+ if (units.length === 0) {
13520
14094
  throw new SqliteError("empty statement", "misuse");
13521
14095
  }
13522
- if (statements.length > 1) {
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(this, sql, statements);
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;