@crvouga/sqlite-mem 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -91,6 +91,7 @@ var KEYWORDS = {
91
91
  FOREIGN: "FOREIGN",
92
92
  FROM: "FROM",
93
93
  FULL: "FULL",
94
+ FALSE: "FALSE",
94
95
  GENERATED: "GENERATED",
95
96
  GLOB: "GLOB",
96
97
  GROUP: "GROUP",
@@ -165,6 +166,7 @@ var KEYWORDS = {
165
166
  TO: "TO",
166
167
  TRANSACTION: "TRANSACTION",
167
168
  TRIGGER: "TRIGGER",
169
+ TRUE: "TRUE",
168
170
  UNBOUNDED: "UNBOUNDED",
169
171
  UNION: "UNION",
170
172
  UNIQUE: "UNIQUE",
@@ -519,6 +521,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
519
521
  "FOREIGN",
520
522
  "FROM",
521
523
  "FULL",
524
+ "FALSE",
522
525
  "GENERATED",
523
526
  "GLOB",
524
527
  "GROUP",
@@ -593,6 +596,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
593
596
  "TO",
594
597
  "TRANSACTION",
595
598
  "TRIGGER",
599
+ "TRUE",
596
600
  "UNBOUNDED",
597
601
  "UNION",
598
602
  "UNIQUE",
@@ -741,13 +745,20 @@ var Parser = class {
741
745
  const stmt = this.parseStatement();
742
746
  return { type: "explain", queryPlan, statement: stmt };
743
747
  }
744
- if (this.check("WITH", "SELECT")) return this.parseSelectStmt();
748
+ if (this.at("WITH")) {
749
+ const withClause = this.parseWithClause();
750
+ if (this.at("SELECT")) return this.parseSelectStmt(withClause);
751
+ if (this.at("INSERT") || this.at("REPLACE")) return this.parseInsertStmt(withClause);
752
+ if (this.at("UPDATE")) return this.parseUpdateStmt(withClause);
753
+ if (this.at("DELETE")) return this.parseDeleteStmt(withClause);
754
+ this.syntaxError("expected SELECT, INSERT, UPDATE, or DELETE after WITH clause");
755
+ }
745
756
  if (this.at("SELECT")) return this.parseSelectStmt();
746
- if (this.check("WITH", "INSERT") || this.at("INSERT") || this.at("REPLACE")) {
757
+ if (this.at("INSERT") || this.at("REPLACE")) {
747
758
  return this.parseInsertStmt();
748
759
  }
749
- if (this.check("WITH", "UPDATE") || this.at("UPDATE")) return this.parseUpdateStmt();
750
- if (this.check("WITH", "DELETE") || this.at("DELETE")) return this.parseDeleteStmt();
760
+ if (this.at("UPDATE")) return this.parseUpdateStmt();
761
+ if (this.at("DELETE")) return this.parseDeleteStmt();
751
762
  if (this.at("CREATE")) return this.parseCreateStmt();
752
763
  if (this.at("DROP")) return this.parseDropStmt();
753
764
  if (this.at("ALTER")) return this.parseAlterTableStmt();
@@ -809,12 +820,15 @@ var Parser = class {
809
820
  this.expect("RPAREN");
810
821
  }
811
822
  this.expect("AS");
812
- if (this.match("NOT")) this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
813
- else this.match("MATERIALIZED");
823
+ let materialized = null;
824
+ if (this.match("NOT")) {
825
+ this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
826
+ materialized = "not_materialized";
827
+ } else if (this.match("MATERIALIZED")) materialized = "materialized";
814
828
  this.expect("LPAREN");
815
- const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.parseSelectCore();
829
+ const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.at("WITH") ? this.parseSelectStmt() : this.parseSelectCore();
816
830
  this.expect("RPAREN");
817
- ctes.push({ name, columns, select });
831
+ ctes.push({ name, columns, materialized, select });
818
832
  } while (this.match("COMMA"));
819
833
  return { recursive, ctes };
820
834
  }
@@ -822,8 +836,7 @@ var Parser = class {
822
836
  return this.at("WITH") ? this.parseWithClause() : null;
823
837
  }
824
838
  // ── SELECT ──────────────────────────────────────────────────────────────
825
- parseSelectStmt() {
826
- const withClause = this.parseOptionalWith();
839
+ parseSelectStmt(withClause = this.parseOptionalWith()) {
827
840
  const select = this.parseSelectCore();
828
841
  select.with = withClause;
829
842
  return select;
@@ -1121,8 +1134,7 @@ var Parser = class {
1121
1134
  return { limit: first, offset };
1122
1135
  }
1123
1136
  // ── INSERT / REPLACE ────────────────────────────────────────────────────
1124
- parseInsertStmt() {
1125
- const withClause = this.parseOptionalWith();
1137
+ parseInsertStmt(withClause = this.parseOptionalWith()) {
1126
1138
  let mode = "insert";
1127
1139
  if (this.match("REPLACE")) {
1128
1140
  mode = "replace";
@@ -1225,8 +1237,7 @@ var Parser = class {
1225
1237
  return this.parseResultColumns();
1226
1238
  }
1227
1239
  // ── UPDATE ──────────────────────────────────────────────────────────────
1228
- parseUpdateStmt() {
1229
- const withClause = this.parseOptionalWith();
1240
+ parseUpdateStmt(withClause = this.parseOptionalWith()) {
1230
1241
  this.expect("UPDATE");
1231
1242
  const or = this.mapUpdateOr(this.parseOrConflict());
1232
1243
  const table = this.parseTableName();
@@ -1241,8 +1252,7 @@ var Parser = class {
1241
1252
  return { type: "update", with: withClause, or, table, alias, set, from, where, returning };
1242
1253
  }
1243
1254
  // ── DELETE ──────────────────────────────────────────────────────────────
1244
- parseDeleteStmt() {
1245
- const withClause = this.parseOptionalWith();
1255
+ parseDeleteStmt(withClause = this.parseOptionalWith()) {
1246
1256
  this.expect("DELETE");
1247
1257
  this.expect("FROM");
1248
1258
  const table = this.parseTableName();
@@ -1911,6 +1921,24 @@ var Parser = class {
1911
1921
  left = this.parseLikeRhs(left, true, "GLOB");
1912
1922
  continue;
1913
1923
  }
1924
+ if (n.kind === "REGEXP") {
1925
+ this.advance();
1926
+ this.advance();
1927
+ const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
1928
+ left = {
1929
+ type: "unary",
1930
+ op: "NOT",
1931
+ expr: {
1932
+ type: "function",
1933
+ name: "REGEXP",
1934
+ distinct: false,
1935
+ args: [pattern, left],
1936
+ orderBy: [],
1937
+ filter: null
1938
+ }
1939
+ };
1940
+ continue;
1941
+ }
1914
1942
  if (n.kind === "BETWEEN") {
1915
1943
  this.advance();
1916
1944
  this.advance();
@@ -1939,6 +1967,14 @@ var Parser = class {
1939
1967
  continue;
1940
1968
  }
1941
1969
  const not = this.match("NOT");
1970
+ if (this.match("TRUE")) {
1971
+ left = { type: "is_bool", expr: left, not, sense: true };
1972
+ continue;
1973
+ }
1974
+ if (this.match("FALSE")) {
1975
+ left = { type: "is_bool", expr: left, not, sense: false };
1976
+ continue;
1977
+ }
1942
1978
  const right2 = this.parseIsRhs();
1943
1979
  left = { type: "binary", op: not ? "IS NOT" : "IS", left, right: right2 };
1944
1980
  continue;
@@ -1958,6 +1994,12 @@ var Parser = class {
1958
1994
  left = this.parseLikeRhs(left, false, "GLOB");
1959
1995
  continue;
1960
1996
  }
1997
+ if (this.at("REGEXP") && PREC.IS_IN_LIKE >= minPrec) {
1998
+ this.advance();
1999
+ const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
2000
+ left = { type: "function", name: "REGEXP", distinct: false, args: [pattern, left], orderBy: [], filter: null };
2001
+ continue;
2002
+ }
1961
2003
  if (this.at("MATCH") && PREC.IS_IN_LIKE >= minPrec) {
1962
2004
  this.advance();
1963
2005
  const right2 = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
@@ -2116,7 +2158,7 @@ var Parser = class {
2116
2158
  }
2117
2159
  if (this.at("CURRENT_DATE") || this.at("CURRENT_TIME") || this.at("CURRENT_TIMESTAMP")) {
2118
2160
  const tok = this.advance();
2119
- return { type: "function", name: tok.value, distinct: false, args: [], filter: null };
2161
+ return { type: "function", name: tok.value, distinct: false, args: [], orderBy: [], filter: null };
2120
2162
  }
2121
2163
  if (this.at("NUMBER")) {
2122
2164
  const tok = this.advance();
@@ -2199,6 +2241,7 @@ var Parser = class {
2199
2241
  } while (this.match("COMMA"));
2200
2242
  }
2201
2243
  }
2244
+ const orderBy = this.parseOrderBy();
2202
2245
  this.expect("RPAREN");
2203
2246
  if (this.match("FILTER")) {
2204
2247
  this.expect("LPAREN");
@@ -2219,13 +2262,13 @@ var Parser = class {
2219
2262
  window = this.parseWindowSpec();
2220
2263
  this.expect("RPAREN");
2221
2264
  }
2222
- const func = isAgg ? { type: "aggregate", name: upper, distinct, args, filter } : { type: "function", name, distinct, args, filter };
2265
+ const func = isAgg ? { type: "aggregate", name: upper, distinct, args, orderBy, filter } : { type: "function", name, distinct, args, orderBy, filter };
2223
2266
  return { type: "window", func, window };
2224
2267
  }
2225
2268
  if (isAgg) {
2226
- return { type: "aggregate", name: upper, distinct, args, filter };
2269
+ return { type: "aggregate", name: upper, distinct, args, orderBy, filter };
2227
2270
  }
2228
- return { type: "function", name, distinct, args, filter };
2271
+ return { type: "function", name, distinct, args, orderBy, filter };
2229
2272
  }
2230
2273
  parseCaseExpr(base) {
2231
2274
  const whens = [];
@@ -2333,8 +2376,12 @@ function fixedClock(instant = DEFAULT_NOW) {
2333
2376
  if (Number.isNaN(ms)) throw new RangeError("invalid clock instant");
2334
2377
  return () => new Date(ms);
2335
2378
  }
2379
+ function systemClock() {
2380
+ return () => /* @__PURE__ */ new Date();
2381
+ }
2336
2382
  function resolveClock(now) {
2337
2383
  if (now === void 0) return fixedClock(DEFAULT_NOW);
2384
+ if (now === "system") return systemClock();
2338
2385
  if (typeof now === "function") return () => new Date(now().getTime());
2339
2386
  return fixedClock(now);
2340
2387
  }
@@ -2399,6 +2446,28 @@ var Prng = class _Prng {
2399
2446
  return copy;
2400
2447
  }
2401
2448
  };
2449
+ var OsEntropy = class _OsEntropy extends Prng {
2450
+ constructor() {
2451
+ super(1);
2452
+ }
2453
+ nextU64() {
2454
+ const bytes = new Uint8Array(8);
2455
+ crypto.getRandomValues(bytes);
2456
+ let value = 0n;
2457
+ for (let i = 0; i < 8; i++) {
2458
+ value |= BigInt(bytes[i]) << BigInt(i * 8);
2459
+ }
2460
+ return BigInt.asUintN(64, value);
2461
+ }
2462
+ getState() {
2463
+ return 0n;
2464
+ }
2465
+ setState(_state) {
2466
+ }
2467
+ clone() {
2468
+ return new _OsEntropy();
2469
+ }
2470
+ };
2402
2471
 
2403
2472
  // src/types/value.ts
2404
2473
  var SqlReal = class {
@@ -2505,6 +2574,23 @@ function applyAffinity(value, affinity) {
2505
2574
  return value;
2506
2575
  }
2507
2576
  }
2577
+ function applyComparisonAffinity(left, right, leftAffinity, rightAffinity) {
2578
+ const leftNumeric = leftAffinity === "INTEGER" || leftAffinity === "REAL" || leftAffinity === "NUMERIC";
2579
+ const rightNumeric = rightAffinity === "INTEGER" || rightAffinity === "REAL" || rightAffinity === "NUMERIC";
2580
+ const leftNone = leftAffinity === null || leftAffinity === "BLOB";
2581
+ const rightNone = rightAffinity === null || rightAffinity === "BLOB";
2582
+ if (leftNumeric && (rightAffinity === "TEXT" || rightNone)) {
2583
+ const affinity = leftAffinity === "REAL" ? "REAL" : "NUMERIC";
2584
+ return [left, applyAffinity(right, affinity)];
2585
+ }
2586
+ if (rightNumeric && (leftAffinity === "TEXT" || leftNone)) {
2587
+ const affinity = rightAffinity === "REAL" ? "REAL" : "NUMERIC";
2588
+ return [applyAffinity(left, affinity), right];
2589
+ }
2590
+ if (leftAffinity === "TEXT" && rightNone) return [left, applyAffinity(right, "TEXT")];
2591
+ if (rightAffinity === "TEXT" && leftNone) return [applyAffinity(left, "TEXT"), right];
2592
+ return [left, right];
2593
+ }
2508
2594
  function coerceToNumber(value) {
2509
2595
  if (value === null) return null;
2510
2596
  if (value instanceof SqlReal) return value.value;
@@ -2661,14 +2747,18 @@ function jsonErrorPosition(input) {
2661
2747
  }
2662
2748
  }
2663
2749
  function isValidJsonText(input, flags = 0) {
2664
- const allowJson5 = (flags & 2) !== 0 || (flags & 1) !== 0;
2750
+ const allowCanonical = flags === 0 || (flags & 1) !== 0;
2751
+ const allowJson5 = (flags & 2) !== 0;
2665
2752
  try {
2666
2753
  if (allowJson5) {
2667
2754
  parseJsonText(input);
2668
2755
  return true;
2669
2756
  }
2670
- parseJsonText(input, { strictCanonical: true });
2671
- return true;
2757
+ if (allowCanonical) {
2758
+ parseJsonText(input, { strictCanonical: true });
2759
+ return true;
2760
+ }
2761
+ return false;
2672
2762
  } catch {
2673
2763
  return false;
2674
2764
  }
@@ -4197,7 +4287,11 @@ function applyModifier(date, modifier) {
4197
4287
  const normalized = modifier.trim().toLowerCase();
4198
4288
  if (normalized === "unixepoch") return new Date((date.getTime() / 864e5 + JULIAN_UNIX_EPOCH) * 1e3);
4199
4289
  if (normalized === "utc" || normalized === "localtime") return result;
4200
- if (normalized === "start of day") result.setUTCHours(0, 0, 0, 0);
4290
+ const weekday = /^weekday\s+([0-6])$/.exec(normalized);
4291
+ if (weekday) {
4292
+ const target = Number(weekday[1]);
4293
+ result.setUTCDate(result.getUTCDate() + (target - result.getUTCDay() + 7) % 7);
4294
+ } else if (normalized === "start of day") result.setUTCHours(0, 0, 0, 0);
4201
4295
  else if (normalized === "start of month") {
4202
4296
  result.setUTCDate(1);
4203
4297
  result.setUTCHours(0, 0, 0, 0);
@@ -4655,13 +4749,15 @@ var mathFunctions = {
4655
4749
  function escapeRegexChar(char) {
4656
4750
  return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
4657
4751
  }
4658
- function likeLiteral(char) {
4659
- const code = char.codePointAt(0);
4660
- if (code >= 65 && code <= 90) return `[${char}${char.toLowerCase()}]`;
4661
- if (code >= 97 && code <= 122) return `[${char}${char.toUpperCase()}]`;
4752
+ function likeLiteral(char, caseSensitive) {
4753
+ if (!caseSensitive) {
4754
+ const code = char.codePointAt(0);
4755
+ if (code >= 65 && code <= 90) return `[${char}${char.toLowerCase()}]`;
4756
+ if (code >= 97 && code <= 122) return `[${char}${char.toUpperCase()}]`;
4757
+ }
4662
4758
  return escapeRegexChar(char);
4663
4759
  }
4664
- function likeMatch(text2, pattern, escape = null) {
4760
+ function likeMatch(text2, pattern, escape = null, caseSensitive = false) {
4665
4761
  if (escape !== null && [...escape].length !== 1) {
4666
4762
  throw new SqliteError("ESCAPE expression must be a single character", "other");
4667
4763
  }
@@ -4671,13 +4767,13 @@ function likeMatch(text2, pattern, escape = null) {
4671
4767
  const char = chars[i];
4672
4768
  if (escape !== null && char === escape) {
4673
4769
  const next = chars[++i];
4674
- source += next === void 0 ? likeLiteral(char) : likeLiteral(next);
4770
+ source += next === void 0 ? likeLiteral(char, caseSensitive) : likeLiteral(next, caseSensitive);
4675
4771
  } else if (char === "%") {
4676
4772
  source += "[\\s\\S]*";
4677
4773
  } else if (char === "_") {
4678
4774
  source += "[\\s\\S]";
4679
4775
  } else {
4680
- source += likeLiteral(char);
4776
+ source += likeLiteral(char, caseSensitive);
4681
4777
  }
4682
4778
  }
4683
4779
  return new RegExp(`${source}$`).test(text2);
@@ -4984,11 +5080,11 @@ var scalarFunctions = {
4984
5080
  requireArgs3("load_extension", args, 1, 2);
4985
5081
  throw new SqliteError("not authorized", "misuse");
4986
5082
  },
4987
- like(args) {
5083
+ like(args, context) {
4988
5084
  requireArgs3("like", args, 2, 3);
4989
5085
  if (args[0] === null || args[1] === null || args[2] === null) return null;
4990
5086
  const escape = args[2] === void 0 ? null : text(args[2]);
4991
- return likeMatch(text(args[1]), text(args[0]), escape) ? 1 : 0;
5087
+ return likeMatch(text(args[1]), text(args[0]), escape, context.caseSensitiveLike === true) ? 1 : 0;
4992
5088
  },
4993
5089
  glob(args) {
4994
5090
  requireArgs3("glob", args, 2);
@@ -5333,7 +5429,8 @@ function sqlOr(left, right) {
5333
5429
  if (leftTruth === null || rightTruth === null) return null;
5334
5430
  return 0;
5335
5431
  }
5336
- function compareResult(op, left, right, collation) {
5432
+ function compareResult(op, left, right, collation, leftAffinity = null, rightAffinity = null) {
5433
+ [left, right] = applyComparisonAffinity(left, right, leftAffinity, rightAffinity);
5337
5434
  if (op === "IS" || op === "IS NOT" || op === "IS DISTINCT FROM" || op === "IS NOT DISTINCT FROM") {
5338
5435
  const equal = left === null || right === null ? left === right : (collation ? compareWithCollation(left, right, collation) : compareSql(left, right)) === 0;
5339
5436
  if (op === "IS" || op === "IS NOT DISTINCT FROM") return booleanValue(equal);
@@ -5386,11 +5483,18 @@ function evalBinary(op, leftExpr, rightExpr, ctx) {
5386
5483
  if (["=", "==", "!=", "<>", "<", "<=", ">", ">=", "IS", "IS NOT", "IS DISTINCT FROM", "IS NOT DISTINCT FROM"].includes(
5387
5484
  op
5388
5485
  )) {
5389
- return compareResult(op, left, right, resolveComparisonCollation(leftExpr, rightExpr, ctx) ?? void 0);
5486
+ return compareResult(
5487
+ op,
5488
+ left,
5489
+ right,
5490
+ resolveComparisonCollation(leftExpr, rightExpr, ctx) ?? void 0,
5491
+ resolveComparisonAffinity(leftExpr, ctx),
5492
+ resolveComparisonAffinity(rightExpr, ctx)
5493
+ );
5390
5494
  }
5391
5495
  if (op === "LIKE" || op === "NOT LIKE" || op === "GLOB" || op === "NOT GLOB") {
5392
5496
  if (left === null || right === null) return null;
5393
- const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right)) : globMatch(textValue(left), textValue(right));
5497
+ const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right), null, ctx.functionContext?.caseSensitiveLike === true) : globMatch(textValue(left), textValue(right));
5394
5498
  return booleanValue(op.startsWith("NOT") ? !matches : matches);
5395
5499
  }
5396
5500
  if (left === null || right === null) return null;
@@ -5527,6 +5631,8 @@ function explicitCollation(expr) {
5527
5631
  case "unary":
5528
5632
  case "cast":
5529
5633
  return explicitCollation(expr.expr);
5634
+ case "is_bool":
5635
+ return explicitCollation(expr.expr);
5530
5636
  case "binary":
5531
5637
  return explicitCollation(expr.left) ?? explicitCollation(expr.right);
5532
5638
  case "between":
@@ -5557,18 +5663,32 @@ function inheritedCollation(expr, ctx) {
5557
5663
  case "unary":
5558
5664
  case "cast":
5559
5665
  return inheritedCollation(expr.expr, ctx);
5666
+ case "is_bool":
5667
+ return inheritedCollation(expr.expr, ctx);
5668
+ default:
5669
+ return null;
5670
+ }
5671
+ }
5672
+ function resolveComparisonAffinity(expr, ctx) {
5673
+ switch (expr.type) {
5674
+ case "column":
5675
+ return ctx.resolveAffinity?.(expr.table, expr.name) ?? ctx.parent?.resolveAffinity?.(expr.table, expr.name) ?? null;
5676
+ case "cast":
5677
+ return affinityFromTypeName(expr.typeName);
5678
+ case "collate":
5679
+ return resolveComparisonAffinity(expr.expr, ctx);
5560
5680
  default:
5561
5681
  return null;
5562
5682
  }
5563
5683
  }
5564
- function evalIn(left, values, not) {
5684
+ function evalIn(left, values, not, leftAffinity) {
5565
5685
  if (values.length === 0) return booleanValue(not);
5566
5686
  if (left === null) return null;
5567
5687
  let sawNull = false;
5568
5688
  for (const value of values) {
5569
5689
  if (value === null) {
5570
5690
  sawNull = true;
5571
- } else if (compareSql(left, value) === 0) {
5691
+ } else if (compareSql(...applyComparisonAffinity(left, value, leftAffinity, null)) === 0) {
5572
5692
  return booleanValue(!not);
5573
5693
  }
5574
5694
  }
@@ -5602,13 +5722,21 @@ function evalExpr(expr, ctx) {
5602
5722
  if (expr.op === "-") return asNumber(-numberValue(value));
5603
5723
  return ~integerValue(value);
5604
5724
  }
5725
+ case "is_bool": {
5726
+ const truth = isTruthySql(evalExpr(expr.expr, ctx));
5727
+ if (!expr.not && expr.sense) return booleanValue(truth === true);
5728
+ if (!expr.not && !expr.sense) return booleanValue(truth === false);
5729
+ if (expr.not && expr.sense) return booleanValue(truth !== true);
5730
+ return booleanValue(truth !== false);
5731
+ }
5605
5732
  case "binary":
5606
5733
  return evalBinary(expr.op, expr.left, expr.right, ctx);
5607
5734
  case "between": {
5608
5735
  const value = evalExpr(expr.expr, ctx);
5609
5736
  const collation = resolveComparisonCollation(expr.expr, expr.lower, ctx) ?? resolveComparisonCollation(expr.expr, expr.upper, ctx) ?? void 0;
5610
- const lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation);
5611
- const result = sqlAnd(lower, () => compareResult("<=", value, evalExpr(expr.upper, ctx), collation));
5737
+ const affinity = resolveComparisonAffinity(expr.expr, ctx);
5738
+ const lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation, affinity);
5739
+ const result = sqlAnd(lower, () => compareResult("<=", value, evalExpr(expr.upper, ctx), collation, affinity));
5612
5740
  if (result === null) return null;
5613
5741
  return expr.not ? booleanValue(result === 0) : result;
5614
5742
  }
@@ -5635,14 +5763,19 @@ function evalExpr(expr, ctx) {
5635
5763
  }
5636
5764
  const left = evalExpr(expr.expr, ctx);
5637
5765
  const values = Array.isArray(expr.values) ? expr.values.map((value) => evalExpr(value, ctx)) : executeSelect(ctx, expr.values).rows.map((row) => row[0] ?? null);
5638
- return evalIn(left, values, expr.not);
5766
+ return evalIn(left, values, expr.not, resolveComparisonAffinity(expr.expr, ctx));
5639
5767
  }
5640
5768
  case "like": {
5641
5769
  const value = evalExpr(expr.expr, ctx);
5642
5770
  const pattern = evalExpr(expr.pattern, ctx);
5643
5771
  const escape = expr.escape === null ? null : evalExpr(expr.escape, ctx);
5644
5772
  if (value === null || pattern === null || escape === null && expr.escape !== null) return null;
5645
- const match = expr.op === "LIKE" ? likeMatch(textValue(value), textValue(pattern), escape === null ? null : textValue(escape)) : globMatch(textValue(value), textValue(pattern));
5773
+ const match = expr.op === "LIKE" ? likeMatch(
5774
+ textValue(value),
5775
+ textValue(pattern),
5776
+ escape === null ? null : textValue(escape),
5777
+ ctx.functionContext?.caseSensitiveLike === true
5778
+ ) : globMatch(textValue(value), textValue(pattern));
5646
5779
  return booleanValue(expr.not ? !match : match);
5647
5780
  }
5648
5781
  case "case": {
@@ -6783,7 +6916,7 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6783
6916
  insert(values, rowid) {
6784
6917
  const command = this.detectCommand(values);
6785
6918
  if (command !== null) {
6786
- this.runCommand(command, values);
6919
+ this.runCommand(command, values, rowid);
6787
6920
  return 0;
6788
6921
  }
6789
6922
  const assigned = rowid ?? this.nextRowid++;
@@ -6932,18 +7065,63 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6932
7065
  }
6933
7066
  return parts.join(" ");
6934
7067
  }
6935
- /** FTS3 matchinfo default format 'pcx' simplified blob of 32-bit LE ints. */
6936
- matchinfo(cursor, _format = "pcx") {
7068
+ /** FTS3 matchinfo format encoded as native-style 32-bit little-endian integers. */
7069
+ matchinfo(cursor, format = "pcx") {
6937
7070
  const row = this.rows.get(cursor.rowid);
6938
7071
  const nPhrase = Math.max(1, cursor.phraseTerms.length);
6939
7072
  const nCol = this.columns.length;
6940
- const values = [nPhrase, nCol];
6941
- for (let p = 0; p < nPhrase; p++) {
6942
- for (let c = 0; c < nCol; c++) {
6943
- const col = this.columns[c];
6944
- const term = cursor.phraseTerms[p]?.[0] ?? "";
6945
- const tf = row ? this.termFreq(row, col, term) : 0;
6946
- values.push(tf);
7073
+ const values = [];
7074
+ for (const request of format) {
7075
+ switch (request) {
7076
+ case "p":
7077
+ values.push(nPhrase);
7078
+ break;
7079
+ case "c":
7080
+ values.push(nCol);
7081
+ break;
7082
+ case "s":
7083
+ for (let c = 0; c < nCol; c++) {
7084
+ let longest = 0;
7085
+ let run = 0;
7086
+ for (let p = 0; p < nPhrase; p++) {
7087
+ const phrase = cursor.phraseTerms[p] ?? [];
7088
+ if (row && this.phraseFreq(row, this.columns[c], phrase) > 0) {
7089
+ run++;
7090
+ longest = Math.max(longest, run);
7091
+ } else {
7092
+ run = 0;
7093
+ }
7094
+ }
7095
+ values.push(longest);
7096
+ }
7097
+ break;
7098
+ case "x":
7099
+ for (let p = 0; p < nPhrase; p++) {
7100
+ const phrase = cursor.phraseTerms[p] ?? [];
7101
+ for (let c = 0; c < nCol; c++) {
7102
+ const column = this.columns[c];
7103
+ const localHits = row ? this.phraseFreq(row, column, phrase) : 0;
7104
+ let globalHits = 0;
7105
+ let matchingRows = 0;
7106
+ for (const candidate of this.rows.values()) {
7107
+ const hits = this.phraseFreq(candidate, column, phrase);
7108
+ globalHits += hits;
7109
+ if (hits > 0) matchingRows++;
7110
+ }
7111
+ values.push(localHits, globalHits, matchingRows);
7112
+ }
7113
+ }
7114
+ break;
7115
+ case "y":
7116
+ for (let p = 0; p < nPhrase; p++) {
7117
+ const phrase = cursor.phraseTerms[p] ?? [];
7118
+ for (let c = 0; c < nCol; c++) {
7119
+ values.push(row ? this.phraseFreq(row, this.columns[c], phrase) : 0);
7120
+ }
7121
+ }
7122
+ break;
7123
+ default:
7124
+ throw new SqliteError(`unrecognized matchinfo request: ${request}`, "other");
6947
7125
  }
6948
7126
  }
6949
7127
  const buf = new Uint8Array(values.length * 4);
@@ -6991,13 +7169,9 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6991
7169
  if (!values.has(key)) return null;
6992
7170
  const v = values.get(key);
6993
7171
  if (typeof v !== "string") return null;
6994
- const otherContent = [...values.entries()].some(
6995
- ([k, val]) => k !== key && val !== null && this.columns.some((c) => c.toLowerCase() === k)
6996
- );
6997
- if (otherContent) return null;
6998
7172
  return v;
6999
7173
  }
7000
- runCommand(command, _values) {
7174
+ runCommand(command, _values, rowid) {
7001
7175
  const cmd = command.toLowerCase();
7002
7176
  if (cmd === "optimize") return;
7003
7177
  if (cmd === "rebuild") {
@@ -7012,13 +7186,15 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
7012
7186
  "other"
7013
7187
  );
7014
7188
  }
7015
- for (const rowid of [...this.rows.keys()]) this.delete(rowid);
7189
+ for (const rowid2 of [...this.rows.keys()]) this.delete(rowid2);
7016
7190
  return;
7017
7191
  }
7018
7192
  if (cmd.startsWith("merge=") || cmd.startsWith("automerge=")) {
7019
7193
  throw new SqliteError("SQL logic error", "other");
7020
7194
  }
7021
7195
  if (cmd === "delete") {
7196
+ if (rowid === void 0) throw new SqliteError("SQL logic error", "other");
7197
+ this.delete(rowid);
7022
7198
  return;
7023
7199
  }
7024
7200
  throw new SqliteError("SQL logic error", "other");
@@ -7251,11 +7427,6 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
7251
7427
  }
7252
7428
  return n;
7253
7429
  }
7254
- termFreq(row, column, term) {
7255
- const needle = this.normalizeQueryTerm(term);
7256
- const tokens = row.tokensByColumn.get(column.toLowerCase()) ?? [];
7257
- return tokens.filter((t) => t.term === needle || t.term.startsWith(needle)).length;
7258
- }
7259
7430
  docLength(row) {
7260
7431
  let n = 0;
7261
7432
  for (const col of this.indexedColumns()) n += (row.tokensByColumn.get(col.toLowerCase()) ?? []).length;
@@ -7519,6 +7690,8 @@ var Table = class _Table {
7519
7690
  scanCache = null;
7520
7691
  /** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
7521
7692
  equalityHashes = null;
7693
+ /** Cached maximum rowid. `undefined` means recompute after deleting the maximum. */
7694
+ maximumRowid = null;
7522
7695
  frozen = false;
7523
7696
  constructor(name, columns, options = {}) {
7524
7697
  this.name = name;
@@ -7721,7 +7894,12 @@ var Table = class _Table {
7721
7894
  if (alias) values.set(normalizeColumnName(alias.name), targetKey);
7722
7895
  const candidate = { rowid: targetKey, values };
7723
7896
  this.validate(candidate, key);
7724
- if (targetKey !== key) this.rows.delete(key);
7897
+ if (targetKey !== key) {
7898
+ this.rows.delete(key);
7899
+ if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
7900
+ this.maximumRowid = void 0;
7901
+ }
7902
+ }
7725
7903
  this.rows.set(targetKey, candidate);
7726
7904
  this.advanceNextRowid(targetKey);
7727
7905
  this.reindexEquality(existing, candidate);
@@ -7735,6 +7913,9 @@ var Table = class _Table {
7735
7913
  if (this.withoutRowid) this.clusteredRows.delete(this.makeClusterKey(existing.values));
7736
7914
  this.unindexEquality(existing);
7737
7915
  this.invalidateScan();
7916
+ if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
7917
+ this.maximumRowid = void 0;
7918
+ }
7738
7919
  return this.rows.delete(key);
7739
7920
  }
7740
7921
  *scan() {
@@ -7756,6 +7937,7 @@ var Table = class _Table {
7756
7937
  strict: this.strict
7757
7938
  });
7758
7939
  copy.nextRowid = this.nextRowid;
7940
+ copy.maximumRowid = this.maximumRowid;
7759
7941
  for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
7760
7942
  for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
7761
7943
  return copy;
@@ -7768,6 +7950,7 @@ var Table = class _Table {
7768
7950
  }
7769
7951
  /** Rebuild clustered storage after snapshot decode or bulk load. */
7770
7952
  rebuildClusteredRows() {
7953
+ this.maximumRowid = void 0;
7771
7954
  if (!this.withoutRowid) return;
7772
7955
  this.clusteredRows.clear();
7773
7956
  for (const row of this.rows.values()) {
@@ -7876,11 +8059,23 @@ var Table = class _Table {
7876
8059
  return primary[0];
7877
8060
  }
7878
8061
  allocateRowid() {
8062
+ if (!this.columns.some((column) => column.autoincrement)) {
8063
+ if (this.maximumRowid === void 0) {
8064
+ this.maximumRowid = null;
8065
+ for (const rowid of this.rows.keys()) {
8066
+ if (this.maximumRowid === null || compareRowids(rowid, this.maximumRowid) > 0) this.maximumRowid = rowid;
8067
+ }
8068
+ }
8069
+ return this.maximumRowid === null ? 1 : incrementRowid(this.maximumRowid);
8070
+ }
7879
8071
  let candidate = canonicalRowid(this.nextRowid);
7880
8072
  while (this.rows.has(candidate)) candidate = incrementRowid(candidate);
7881
8073
  return candidate;
7882
8074
  }
7883
8075
  advanceNextRowid(rowid) {
8076
+ if (this.maximumRowid === null || this.maximumRowid !== void 0 && compareRowids(rowid, this.maximumRowid) > 0) {
8077
+ this.maximumRowid = rowid;
8078
+ }
7884
8079
  if (compareRowids(rowid, this.nextRowid) >= 0) this.nextRowid = incrementRowid(rowid);
7885
8080
  }
7886
8081
  };
@@ -7970,6 +8165,8 @@ var DatabaseState = class _DatabaseState {
7970
8165
  changes = 0;
7971
8166
  totalChanges = 0;
7972
8167
  foreignKeysEnabled = false;
8168
+ /** When true, LIKE / like() are case-sensitive (SQLite `PRAGMA case_sensitive_like`). */
8169
+ caseSensitiveLike = false;
7973
8170
  schemaVersion = 0;
7974
8171
  userVersion = 0;
7975
8172
  databaseForSchema(schema, qualifiedForError) {
@@ -8414,6 +8611,7 @@ var DatabaseState = class _DatabaseState {
8414
8611
  copy.changes = this.changes;
8415
8612
  copy.totalChanges = this.totalChanges;
8416
8613
  copy.foreignKeysEnabled = this.foreignKeysEnabled;
8614
+ copy.caseSensitiveLike = this.caseSensitiveLike;
8417
8615
  copy.schemaVersion = this.schemaVersion;
8418
8616
  copy.userVersion = this.userVersion;
8419
8617
  return copy;
@@ -8433,6 +8631,7 @@ var DatabaseState = class _DatabaseState {
8433
8631
  copy.changes = this.changes;
8434
8632
  copy.totalChanges = this.totalChanges;
8435
8633
  copy.foreignKeysEnabled = this.foreignKeysEnabled;
8634
+ copy.caseSensitiveLike = this.caseSensitiveLike;
8436
8635
  copy.schemaVersion = this.schemaVersion;
8437
8636
  copy.userVersion = this.userVersion;
8438
8637
  return copy;
@@ -8494,6 +8693,7 @@ var DatabaseState = class _DatabaseState {
8494
8693
  this.changes = copy.changes;
8495
8694
  this.totalChanges = copy.totalChanges;
8496
8695
  this.foreignKeysEnabled = copy.foreignKeysEnabled;
8696
+ this.caseSensitiveLike = copy.caseSensitiveLike;
8497
8697
  this.schemaVersion = copy.schemaVersion;
8498
8698
  this.userVersion = copy.userVersion;
8499
8699
  }
@@ -8996,8 +9196,9 @@ var ExecutionEnv = class {
8996
9196
  createEvalContext(row = null, parent) {
8997
9197
  const scope = row ?? this.triggerScope;
8998
9198
  const cells = scope?.cells ?? [];
9199
+ const inheritedParent = parent ?? (row !== null && this.triggerScope !== null && row !== this.triggerScope ? this.createEvalContext(this.triggerScope) : void 0);
8999
9200
  const context = {
9000
- parent,
9201
+ parent: inheritedParent,
9001
9202
  ftsMatch: scope?.ftsMatch ?? null,
9002
9203
  functions: this.functions,
9003
9204
  functionContext: {
@@ -9007,6 +9208,7 @@ var ExecutionEnv = class {
9007
9208
  now: this.hooks.now,
9008
9209
  random: this.hooks.random,
9009
9210
  randomU64: this.hooks.randomU64,
9211
+ caseSensitiveLike: this.state.caseSensitiveLike,
9010
9212
  ftsMatch: scope?.ftsMatch ?? null,
9011
9213
  ftsRowid: scope?.rowid,
9012
9214
  ftsSourceTable: scope?.sourceTable,
@@ -9038,8 +9240,13 @@ var ExecutionEnv = class {
9038
9240
  if (table === null) return !cell.hiddenByUsing;
9039
9241
  return (cell.tableLower ?? cell.table?.toLowerCase()) === tableKey;
9040
9242
  });
9041
- if (matches.length === 0)
9243
+ if (matches.length === 0) {
9244
+ if (table === null) {
9245
+ if (key === "true") return 1;
9246
+ if (key === "false") return 0;
9247
+ }
9042
9248
  throw new SqliteError(`no such column: ${table ? `${table}.` : ""}${name}`, "no_such_column");
9249
+ }
9043
9250
  if (matches.length > 1 && table === null) throw new SqliteError(`ambiguous column name: ${name}`, "other");
9044
9251
  return matches[0].value;
9045
9252
  },
@@ -9052,12 +9259,25 @@ var ExecutionEnv = class {
9052
9259
  if (table === null) return !cell2.hiddenByUsing;
9053
9260
  return (cell2.tableLower ?? cell2.table?.toLowerCase()) === tableKey;
9054
9261
  });
9055
- if (matches.length === 0)
9262
+ if (matches.length === 0) {
9263
+ if (table === null && (key === "true" || key === "false")) return "integer";
9056
9264
  throw new SqliteError(`no such column: ${table ? `${table}.` : ""}${name}`, "no_such_column");
9265
+ }
9057
9266
  if (matches.length > 1 && table === null) throw new SqliteError(`ambiguous column name: ${name}`, "other");
9058
9267
  const cell = matches[0];
9059
9268
  return cell.affinity === "REAL" && typeof cell.value === "number" ? "real" : storageClassOf(cell.value);
9060
9269
  },
9270
+ resolveAffinity: (table, name) => {
9271
+ const key = name.toLowerCase();
9272
+ const tableKey = table?.toLowerCase();
9273
+ const matches = cells.filter((cell) => {
9274
+ if ((cell.nameLower ?? cell.name.toLowerCase()) !== key) return false;
9275
+ if (table === null) return !cell.hiddenByUsing;
9276
+ return (cell.tableLower ?? cell.table?.toLowerCase()) === tableKey;
9277
+ });
9278
+ if (matches.length !== 1) return null;
9279
+ return matches[0].affinity ?? null;
9280
+ },
9061
9281
  resolveCollation: (table, name) => {
9062
9282
  const key = name.toLowerCase();
9063
9283
  const tableKey = table?.toLowerCase();
@@ -9465,6 +9685,8 @@ function queryPragma(name, args, env) {
9465
9685
  switch (key) {
9466
9686
  case "foreign_keys":
9467
9687
  return single("foreign_keys", env.state.foreignKeysEnabled ? 1 : 0);
9688
+ case "case_sensitive_like":
9689
+ return single("case_sensitive_like", env.state.caseSensitiveLike ? 1 : 0);
9468
9690
  case "user_version":
9469
9691
  return single("user_version", env.state.userVersion);
9470
9692
  case "schema_version":
@@ -9892,83 +10114,6 @@ function hasTableValuedFunction(name) {
9892
10114
  return hasRegisteredTableValuedFunction(name);
9893
10115
  }
9894
10116
 
9895
- // src/schema/catalog.ts
9896
- function schemaCatalogRows(state) {
9897
- const tables = [...state.tables.values()].sort((a, b) => compareNames2(a.name, b.name));
9898
- const indexes = [...state.indexes.values()].sort((a, b) => compareNames2(a.name, b.name));
9899
- const views = [...state.views.values()].sort((a, b) => compareNames2(a.name, b.name));
9900
- const triggers = [...state.triggers.values()].sort((a, b) => compareNames2(a.name, b.name));
9901
- const rows = [];
9902
- let rootpage = 2;
9903
- for (const table of tables) {
9904
- rows.push({
9905
- type: "table",
9906
- name: table.name,
9907
- tbl_name: table.name,
9908
- rootpage: rootpage++,
9909
- sql: table.originalSql
9910
- });
9911
- }
9912
- for (const index of indexes) {
9913
- rows.push({
9914
- type: "index",
9915
- name: index.name,
9916
- tbl_name: index.tableName,
9917
- rootpage: rootpage++,
9918
- sql: index.originalSql
9919
- });
9920
- }
9921
- for (const view of views) {
9922
- rows.push({
9923
- type: "view",
9924
- name: view.name,
9925
- tbl_name: view.name,
9926
- rootpage: 0,
9927
- sql: view.originalSql
9928
- });
9929
- }
9930
- for (const trigger of triggers) {
9931
- rows.push({
9932
- type: "trigger",
9933
- name: trigger.name,
9934
- tbl_name: trigger.tableName,
9935
- rootpage: 0,
9936
- sql: trigger.originalSql
9937
- });
9938
- }
9939
- return rows;
9940
- }
9941
- function compareNames2(a, b) {
9942
- return a < b ? -1 : a > b ? 1 : 0;
9943
- }
9944
- function buildSchemaCatalog(state, name = "sqlite_schema") {
9945
- const catalog = new Table(name, [
9946
- makeColumnInfo("type", "TEXT"),
9947
- makeColumnInfo("name", "TEXT"),
9948
- makeColumnInfo("tbl_name", "TEXT"),
9949
- makeColumnInfo("rootpage", "INTEGER"),
9950
- makeColumnInfo("sql", "TEXT")
9951
- ]);
9952
- for (const row of schemaCatalogRows(state)) {
9953
- catalog.insert({
9954
- values: {
9955
- type: row.type,
9956
- name: row.name,
9957
- tbl_name: row.tbl_name,
9958
- rootpage: row.rootpage,
9959
- sql: row.sql
9960
- }
9961
- });
9962
- }
9963
- return catalog;
9964
- }
9965
- function buildSqliteSchema(state) {
9966
- return buildSchemaCatalog(state, "sqlite_schema");
9967
- }
9968
- function buildSqliteMaster(state) {
9969
- return buildSchemaCatalog(state, "sqlite_master");
9970
- }
9971
-
9972
10117
  // src/expressions/equals.ts
9973
10118
  function exprEquals(left, right) {
9974
10119
  if (left.type !== right.type) return false;
@@ -9981,6 +10126,8 @@ function exprEquals(left, right) {
9981
10126
  return right.type === "column" && (left.table ?? "").toLowerCase() === (right.table ?? "").toLowerCase() && left.name.toLowerCase() === right.name.toLowerCase();
9982
10127
  case "unary":
9983
10128
  return right.type === "unary" && left.op === right.op && exprEquals(left.expr, right.expr);
10129
+ case "is_bool":
10130
+ return right.type === "is_bool" && left.not === right.not && left.sense === right.sense && exprEquals(left.expr, right.expr);
9984
10131
  case "binary":
9985
10132
  return right.type === "binary" && left.op === right.op && exprEquals(left.left, right.left) && exprEquals(left.right, right.right);
9986
10133
  case "function":
@@ -10360,6 +10507,83 @@ function columnOnTable(column, alias, tableName) {
10360
10507
  return column.table !== null && matchesTable(column.table, alias, tableName);
10361
10508
  }
10362
10509
 
10510
+ // src/schema/catalog.ts
10511
+ function schemaCatalogRows(state) {
10512
+ const tables = [...state.tables.values()].sort((a, b) => compareNames2(a.name, b.name));
10513
+ const indexes = [...state.indexes.values()].sort((a, b) => compareNames2(a.name, b.name));
10514
+ const views = [...state.views.values()].sort((a, b) => compareNames2(a.name, b.name));
10515
+ const triggers = [...state.triggers.values()].sort((a, b) => compareNames2(a.name, b.name));
10516
+ const rows = [];
10517
+ let rootpage = 2;
10518
+ for (const table of tables) {
10519
+ rows.push({
10520
+ type: "table",
10521
+ name: table.name,
10522
+ tbl_name: table.name,
10523
+ rootpage: rootpage++,
10524
+ sql: table.originalSql
10525
+ });
10526
+ }
10527
+ for (const index of indexes) {
10528
+ rows.push({
10529
+ type: "index",
10530
+ name: index.name,
10531
+ tbl_name: index.tableName,
10532
+ rootpage: rootpage++,
10533
+ sql: index.originalSql
10534
+ });
10535
+ }
10536
+ for (const view of views) {
10537
+ rows.push({
10538
+ type: "view",
10539
+ name: view.name,
10540
+ tbl_name: view.name,
10541
+ rootpage: 0,
10542
+ sql: view.originalSql
10543
+ });
10544
+ }
10545
+ for (const trigger of triggers) {
10546
+ rows.push({
10547
+ type: "trigger",
10548
+ name: trigger.name,
10549
+ tbl_name: trigger.tableName,
10550
+ rootpage: 0,
10551
+ sql: trigger.originalSql
10552
+ });
10553
+ }
10554
+ return rows;
10555
+ }
10556
+ function compareNames2(a, b) {
10557
+ return a < b ? -1 : a > b ? 1 : 0;
10558
+ }
10559
+ function buildSchemaCatalog(state, name = "sqlite_schema") {
10560
+ const catalog = new Table(name, [
10561
+ makeColumnInfo("type", "TEXT"),
10562
+ makeColumnInfo("name", "TEXT"),
10563
+ makeColumnInfo("tbl_name", "TEXT"),
10564
+ makeColumnInfo("rootpage", "INTEGER"),
10565
+ makeColumnInfo("sql", "TEXT")
10566
+ ]);
10567
+ for (const row of schemaCatalogRows(state)) {
10568
+ catalog.insert({
10569
+ values: {
10570
+ type: row.type,
10571
+ name: row.name,
10572
+ tbl_name: row.tbl_name,
10573
+ rootpage: row.rootpage,
10574
+ sql: row.sql
10575
+ }
10576
+ });
10577
+ }
10578
+ return catalog;
10579
+ }
10580
+ function buildSqliteSchema(state) {
10581
+ return buildSchemaCatalog(state, "sqlite_schema");
10582
+ }
10583
+ function buildSqliteMaster(state) {
10584
+ return buildSchemaCatalog(state, "sqlite_master");
10585
+ }
10586
+
10363
10587
  // src/executor/simple-select.ts
10364
10588
  function tryExecuteSimpleSelect(stmt, env) {
10365
10589
  if (stmt.with || stmt.compound || stmt.distinct || stmt.groupBy.length > 0 || stmt.having || stmt.windows.length > 0)
@@ -10408,7 +10632,9 @@ function tryExecuteSimpleSelect(stmt, env) {
10408
10632
  } catch {
10409
10633
  return null;
10410
10634
  }
10411
- filters.push({ column: eq.column.toLowerCase(), value });
10635
+ const column = eq.column.toLowerCase();
10636
+ const affinity = isRowidName2(column) ? "INTEGER" : table.columns.find((item) => (item.nameLower ?? item.name.toLowerCase()) === column)?.affinity;
10637
+ filters.push({ column, value: affinity ? applyAffinity(value, affinity) : value });
10412
10638
  }
10413
10639
  }
10414
10640
  let limit = env.maxRows;
@@ -10590,7 +10816,7 @@ function executeSelect2(stmt, env, parent) {
10590
10816
  }
10591
10817
  const savedCtes = new Map(env.ctes);
10592
10818
  try {
10593
- if (stmt.with) executeWith(stmt, env, parent);
10819
+ if (stmt.with) executeWith(stmt.with, env, parent);
10594
10820
  const base = executeSelectCore(
10595
10821
  {
10596
10822
  ...stmt,
@@ -10646,22 +10872,39 @@ function executeSelect2(stmt, env, parent) {
10646
10872
  for (const [name, result] of savedCtes) env.ctes.set(name, result);
10647
10873
  }
10648
10874
  }
10649
- function executeWith(stmt, env, parent) {
10650
- for (const cte of stmt.with.ctes) {
10875
+ function executeWith(withClause, env, parent) {
10876
+ for (const cte of withClause.ctes) {
10651
10877
  const key = cte.name.toLowerCase();
10652
- if (stmt.with.recursive && referencesTable(cte.select, cte.name) && cte.select.compound) {
10878
+ if (withClause.recursive && referencesTable(cte.select, cte.name) && cte.select.compound) {
10653
10879
  const anchor = executeSelect2({ ...cte.select, compound: null }, env, parent);
10654
10880
  const columns = cte.columns ?? anchor.columns;
10655
- let accumulated = resultValues(anchor);
10656
- let delta = accumulated;
10657
- while (true) {
10658
- env.ctes.set(key, valuesToResult(columns, delta));
10659
- const nextResult = executeSelect2(cte.select.compound.select, env, parent);
10881
+ const recursive = cte.select.compound.select;
10882
+ const limit = recursive.limit ? toInteger(evalExpr(recursive.limit.limit, env.createEvalContext(null, parent))) : null;
10883
+ if (recursive.limit && limit === null) throw new SqliteError("datatype mismatch", "datatype_mismatch");
10884
+ const maxRows = limit === null || Number(limit) < 0 ? Number.POSITIVE_INFINITY : Number(limit);
10885
+ const queue = resultValues(anchor);
10886
+ const discovered = [...queue];
10887
+ const accumulated = [];
10888
+ if (recursive.orderBy.length > 0) {
10889
+ queue.sort((left, right) => compareCteQueueRows(left, right, columns, recursive.orderBy, env, parent));
10890
+ }
10891
+ while (queue.length > 0 && accumulated.length < maxRows) {
10892
+ const current = queue.shift();
10893
+ accumulated.push(current);
10894
+ env.ctes.set(key, valuesToResult(columns, [current]));
10895
+ const nextResult = executeSelect2({ ...recursive, orderBy: [], limit: null }, env, parent);
10660
10896
  const candidates = resultValues(nextResult);
10661
- const additions = cte.select.compound.op === "UNION ALL" ? candidates : candidates.filter((row) => !accumulated.some((existing) => rowsEqual2(existing, row)));
10662
- if (additions.length === 0) break;
10663
- accumulated = [...accumulated, ...additions];
10664
- delta = additions;
10897
+ const additions = [];
10898
+ for (const candidate of candidates) {
10899
+ if (cte.select.compound.op !== "UNION ALL" && [...discovered, ...additions].some((existing) => rowsEqual2(existing, candidate)))
10900
+ continue;
10901
+ additions.push(candidate);
10902
+ }
10903
+ discovered.push(...additions);
10904
+ queue.push(...additions);
10905
+ if (recursive.orderBy.length > 0) {
10906
+ queue.sort((left, right) => compareCteQueueRows(left, right, columns, recursive.orderBy, env, parent));
10907
+ }
10665
10908
  }
10666
10909
  env.ctes.set(key, valuesToResult(columns, accumulated));
10667
10910
  } else {
@@ -10671,6 +10914,26 @@ function executeWith(stmt, env, parent) {
10671
10914
  }
10672
10915
  }
10673
10916
  }
10917
+ function compareCteQueueRows(left, right, columns, order, env, parent) {
10918
+ const scope = (values) => ({
10919
+ cells: columns.map((name, index) => ({ table: null, name, value: values[index] ?? null }))
10920
+ });
10921
+ const leftScope = scope(left);
10922
+ const rightScope = scope(right);
10923
+ for (const item of order) {
10924
+ if (item.expr.type === "literal" && typeof item.expr.value === "number" && Number.isInteger(item.expr.value)) {
10925
+ const index = item.expr.value - 1;
10926
+ const result2 = compareNullable(left[index] ?? null, right[index] ?? null, item);
10927
+ if (result2 !== 0) return result2;
10928
+ continue;
10929
+ }
10930
+ const a = evalExpr(item.expr, env.createEvalContext(leftScope, parent));
10931
+ const b = evalExpr(item.expr, env.createEvalContext(rightScope, parent));
10932
+ const result = compareNullable(a, b, item, leftScope);
10933
+ if (result !== 0) return result;
10934
+ }
10935
+ return 0;
10936
+ }
10674
10937
  function executeSelectCore(stmt, env, parent) {
10675
10938
  let scopes;
10676
10939
  let skipWhere = false;
@@ -10726,10 +10989,18 @@ function executeSelectCore(stmt, env, parent) {
10726
10989
  }
10727
10990
  }
10728
10991
  const groupBy = stmt.groupBy.map((expr) => {
10729
- if (expr.type !== "literal" || typeof expr.value !== "number" || !Number.isInteger(expr.value)) return expr;
10730
- const column = stmt.columns[expr.value - 1];
10731
- if (column?.type !== "expr") throw new SqliteError(`${expr.value}th GROUP BY term out of range`, "other");
10732
- return column.expr;
10992
+ if (expr.type === "literal" && typeof expr.value === "number" && Number.isInteger(expr.value)) {
10993
+ const column = stmt.columns[expr.value - 1];
10994
+ if (column?.type !== "expr") throw new SqliteError(`${expr.value}th GROUP BY term out of range`, "other");
10995
+ return column.expr;
10996
+ }
10997
+ if (expr.type === "column" && expr.table === null) {
10998
+ const column = stmt.columns.find(
10999
+ (candidate) => candidate.type === "expr" && candidate.alias?.toLowerCase() === expr.name.toLowerCase()
11000
+ );
11001
+ if (column?.type === "expr") return column.expr;
11002
+ }
11003
+ return expr;
10733
11004
  });
10734
11005
  const groups = aggregate ? groupRows(scopes, groupBy, env, parent) : scopes.map((scope) => [scope]);
10735
11006
  const windowScopes = aggregate ? groups.map((group) => group[0] ?? { cells: [] }) : scopes;
@@ -11186,7 +11457,10 @@ function groupRows(rows, expressions, env, parent) {
11186
11457
  const groups = [];
11187
11458
  const indexByKey = /* @__PURE__ */ new Map();
11188
11459
  for (const row of rows) {
11189
- const keyValues = expressions.map((expr) => evalExpr(expr, env.createEvalContext(row, parent)));
11460
+ const keyValues = expressions.map((expr) => {
11461
+ const value = evalExpr(expr, env.createEvalContext(row, parent));
11462
+ return expr.type === "collate" ? normalizeForCollation(value, expr.collation) : value;
11463
+ });
11190
11464
  const key = valueKey(keyValues);
11191
11465
  const index = indexByKey.get(key);
11192
11466
  if (index === void 0) {
@@ -11209,7 +11483,8 @@ function aggregateValue(expr, rows, env, parent) {
11209
11483
  const accumulator = env.functions.createAggregate(expr.name);
11210
11484
  if (!accumulator) throw new SqliteError(`no such aggregate function: ${expr.name}`, "other");
11211
11485
  const seen = [];
11212
- for (const row of rows) {
11486
+ const orderedRows = expr.orderBy.length > 0 ? [...rows].sort((a, b) => compareScopes(a, b, expr.orderBy, env, parent)) : rows;
11487
+ for (const row of orderedRows) {
11213
11488
  const ctx = env.createEvalContext(row, parent);
11214
11489
  if (expr.filter && isTruthySql(evalExpr(expr.filter, ctx)) !== true) continue;
11215
11490
  const args = expr.args === "*" ? [] : expr.args.map((arg) => evalExpr(arg, ctx));
@@ -11325,8 +11600,10 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
11325
11600
  if (!spec.frame) return [0, spec.orderBy.length > 0 ? defaultFrameEnd : Math.max(0, length - 1)];
11326
11601
  const isRangeLike = spec.frame.type === "RANGE" || spec.frame.type === "GROUPS";
11327
11602
  let peerFirst = index;
11603
+ let peerLast = index;
11328
11604
  if (isRangeLike && spec.orderBy.length > 0) {
11329
11605
  while (peerFirst > 0 && rowsEqual2(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
11606
+ while (peerLast + 1 < length && rowsEqual2(orderKeys[peerLast], orderKeys[peerLast + 1])) peerLast++;
11330
11607
  }
11331
11608
  const bound = (item, isStart) => {
11332
11609
  switch (item.kind) {
@@ -11335,12 +11612,54 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
11335
11612
  case "unbounded_following":
11336
11613
  return Math.max(0, length - 1);
11337
11614
  case "current_row":
11338
- if (isRangeLike) return isStart ? peerFirst : defaultFrameEnd;
11615
+ if (isRangeLike) return isStart ? peerFirst : peerLast;
11339
11616
  return index;
11340
11617
  case "preceding":
11341
- return Math.max(0, index - Number(toInteger(evalExpr(item.expr, ctx)) ?? 0));
11342
- case "following":
11343
- return Math.min(Math.max(0, length - 1), index + Number(toInteger(evalExpr(item.expr, ctx)) ?? 0));
11618
+ case "following": {
11619
+ const offset = Number(evalExpr(item.expr, ctx));
11620
+ if (spec.frame?.type === "GROUPS") {
11621
+ const groups = [];
11622
+ for (let i = 0; i < length; ) {
11623
+ let last = i;
11624
+ while (last + 1 < length && rowsEqual2(orderKeys[last], orderKeys[last + 1])) last++;
11625
+ groups.push({ first: i, last });
11626
+ i = last + 1;
11627
+ }
11628
+ const currentGroup = groups.findIndex((group) => index >= group.first && index <= group.last);
11629
+ const delta = item.kind === "preceding" ? -offset : offset;
11630
+ const target = groups[Math.max(0, Math.min(groups.length - 1, currentGroup + delta))];
11631
+ return isStart ? target.first : target.last;
11632
+ }
11633
+ if (spec.frame?.type === "RANGE" && spec.orderBy.length === 1) {
11634
+ const rawCurrent = orderKeys[index]?.[0];
11635
+ const current = typeof rawCurrent === "number" ? rawCurrent : typeof rawCurrent === "bigint" ? Number(rawCurrent) : rawCurrent && typeof rawCurrent === "object" && "value" in rawCurrent ? Number(rawCurrent.value) : Number.NaN;
11636
+ if (!Number.isFinite(current) || !Number.isFinite(offset)) return isStart ? peerFirst : peerLast;
11637
+ const descending = spec.orderBy[0]?.dir === "DESC";
11638
+ const signed = item.kind === "preceding" ? -offset : offset;
11639
+ const target = current + (descending ? -signed : signed);
11640
+ const numericKey = (position) => {
11641
+ const value = orderKeys[position]?.[0];
11642
+ if (typeof value === "number") return value;
11643
+ if (typeof value === "bigint") return Number(value);
11644
+ if (value && typeof value === "object" && "value" in value) return Number(value.value);
11645
+ return Number.NaN;
11646
+ };
11647
+ if (isStart) {
11648
+ for (let i = 0; i < length; i++) {
11649
+ const value = numericKey(i);
11650
+ if (descending && value <= target || !descending && value >= target) return i;
11651
+ }
11652
+ return length;
11653
+ }
11654
+ for (let i = length - 1; i >= 0; i--) {
11655
+ const value = numericKey(i);
11656
+ if (descending && value >= target || !descending && value <= target) return i;
11657
+ }
11658
+ return -1;
11659
+ }
11660
+ const rowOffset = Number(toInteger(evalExpr(item.expr, ctx)) ?? 0);
11661
+ return item.kind === "preceding" ? Math.max(0, index - rowOffset) : Math.min(Math.max(0, length - 1), index + rowOffset);
11662
+ }
11344
11663
  }
11345
11664
  };
11346
11665
  return [bound(spec.frame.start, true), bound(spec.frame.end, false)];
@@ -11445,6 +11764,9 @@ function validateProjectedColumns(stmt, sample, env, parent) {
11445
11764
  case "unary":
11446
11765
  recurse(expr.expr);
11447
11766
  break;
11767
+ case "is_bool":
11768
+ recurse(expr.expr);
11769
+ break;
11448
11770
  case "binary":
11449
11771
  recurse(expr.left);
11450
11772
  recurse(expr.right);
@@ -11507,6 +11829,10 @@ function expressionName(expr) {
11507
11829
  if (expr.type === "binary") {
11508
11830
  return `${expressionName(expr.left)} ${expr.op} ${expressionName(expr.right)}`;
11509
11831
  }
11832
+ if (expr.type === "is_bool") {
11833
+ const sense = expr.sense ? "TRUE" : "FALSE";
11834
+ return `${expressionName(expr.expr)} IS${expr.not ? " NOT" : ""} ${sense}`;
11835
+ }
11510
11836
  return expr.type;
11511
11837
  }
11512
11838
  function replaceSpecial(expr, evaluate) {
@@ -11515,6 +11841,8 @@ function replaceSpecial(expr, evaluate) {
11515
11841
  switch (expr.type) {
11516
11842
  case "unary":
11517
11843
  return { ...expr, expr: recurse(expr.expr) };
11844
+ case "is_bool":
11845
+ return { ...expr, expr: recurse(expr.expr) };
11518
11846
  case "binary":
11519
11847
  return { ...expr, left: recurse(expr.left), right: recurse(expr.right) };
11520
11848
  case "between":
@@ -11958,6 +12286,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
11958
12286
  }
11959
12287
  env.triggerDepth++;
11960
12288
  const savedScope = env.triggerScope;
12289
+ const savedLastInsertRowid = env.state.lastInsertRowid;
11961
12290
  env.triggerScope = triggerScope(table, oldRow, newValues);
11962
12291
  try {
11963
12292
  for (const statement of trigger.body) {
@@ -11975,6 +12304,7 @@ function executeTriggerProgram(trigger, table, oldRow, newValues, env) {
11975
12304
  }
11976
12305
  throw error;
11977
12306
  } finally {
12307
+ env.state.lastInsertRowid = savedLastInsertRowid;
11978
12308
  env.triggerScope = savedScope;
11979
12309
  env.triggerDepth--;
11980
12310
  }
@@ -12069,9 +12399,20 @@ function storeColumnValue(table, column, value) {
12069
12399
  return applyAffinity(value, column.affinity);
12070
12400
  }
12071
12401
  function executeInsert(stmt, env) {
12402
+ try {
12403
+ return withDmlCtes(stmt.with, env, () => executeInsertCore(stmt, env));
12404
+ } catch (error) {
12405
+ handleConflictRollback(stmt.mode === "insert_or_rollback", error, env);
12406
+ throw error;
12407
+ }
12408
+ }
12409
+ function executeInsertCore(stmt, env) {
12072
12410
  if (env.state.isVirtualTable(stmt.table)) {
12073
12411
  return executeVirtualInsert(stmt, env);
12074
12412
  }
12413
+ const totalBefore = env.state.totalChanges;
12414
+ const view = writableView(stmt.table, "INSERT", env);
12415
+ if (view) return executeViewInsert(stmt, view, env, totalBefore);
12075
12416
  const fast = tryFastInsert(stmt, env);
12076
12417
  if (fast) return fast;
12077
12418
  const table = env.state.getWritableTable(stmt.table);
@@ -12089,11 +12430,14 @@ function executeInsert(stmt, env) {
12089
12430
  const suppliedIndexes = table.columns.map(
12090
12431
  (column) => columnNames.findIndex((name) => name.toLowerCase() === (column.nameLower ?? column.name.toLowerCase()))
12091
12432
  );
12092
- const unconstrained = table.isUnconstrained() && !stmt.upsert && stmt.mode === "insert" && stmt.returning.length === 0 && rowidIndexes.length === 0;
12433
+ const unconstrained = table.isUnconstrained() && env.state.databaseForTable(table).triggers.size === 0 && !stmt.upsert && stmt.mode === "insert" && stmt.returning.length === 0 && rowidIndexes.length === 0;
12093
12434
  if (unconstrained) {
12094
12435
  for (const source of sourceRows) {
12095
12436
  if (source.length !== columnNames.length)
12096
- throw new SqliteError(`${source.length} values for ${columnNames.length} columns`, "other");
12437
+ throw new SqliteError(
12438
+ stmt.columns ? `${source.length} values for ${columnNames.length} columns` : `table ${table.name} has ${columnNames.length} columns but ${source.length} values were supplied`,
12439
+ "other"
12440
+ );
12097
12441
  const values = /* @__PURE__ */ new Map();
12098
12442
  for (let index = 0; index < table.columns.length; index++) {
12099
12443
  const column = table.columns[index];
@@ -12109,7 +12453,10 @@ function executeInsert(stmt, env) {
12109
12453
  }
12110
12454
  for (const source of sourceRows) {
12111
12455
  if (source.length !== columnNames.length)
12112
- throw new SqliteError(`${source.length} values for ${columnNames.length} columns`, "other");
12456
+ throw new SqliteError(
12457
+ stmt.columns ? `${source.length} values for ${columnNames.length} columns` : `table ${table.name} has ${columnNames.length} columns but ${source.length} values were supplied`,
12458
+ "other"
12459
+ );
12113
12460
  const values = /* @__PURE__ */ new Map();
12114
12461
  for (const column of table.columns) {
12115
12462
  if (column.generated) continue;
@@ -12197,9 +12544,12 @@ function executeInsert(stmt, env) {
12197
12544
  validateRow(table, row, env);
12198
12545
  if (table.indexes.length > 0) addIndexes(table, row, env);
12199
12546
  if (env.state.foreignKeysEnabled) checkForeignKeys(table, row, env);
12547
+ if (!table.withoutRowid) {
12548
+ last = rowid;
12549
+ env.state.lastInsertRowid = rowid;
12550
+ }
12200
12551
  fireInsertTriggers("AFTER", table, row.values, null, env);
12201
12552
  changes++;
12202
- last = table.withoutRowid ? 0 : rowid;
12203
12553
  if (stmt.returning.length)
12204
12554
  returningRows.push(projectReturning(stmt.returning, scopeFor(table, row, stmt.table, env), env));
12205
12555
  } catch (error) {
@@ -12212,12 +12562,12 @@ function executeInsert(stmt, env) {
12212
12562
  throw error;
12213
12563
  }
12214
12564
  }
12215
- env.state.recordChange(changes, last);
12216
- if (stmt.returning.length === 0) return emptyResult(changes, last);
12217
- return valuesToResult(returningNames(stmt.returning, table), returningRows, changes, last);
12565
+ const reportedChanges = finalizeDmlChanges(totalBefore, changes, env, last);
12566
+ if (stmt.returning.length === 0) return emptyResult(reportedChanges, last);
12567
+ return valuesToResult(returningNames(stmt.returning, table), returningRows, reportedChanges, last);
12218
12568
  }
12219
12569
  function evaluateInsertSource(stmt, env) {
12220
- if (stmt.select) return resultValues(executeSelect2({ ...stmt.select, with: stmt.with ?? stmt.select.with }, env));
12570
+ if (stmt.select) return resultValues(executeSelect2(stmt.select, env));
12221
12571
  if (!stmt.values) return [[]];
12222
12572
  const ctx = env.createEvalContext();
12223
12573
  return stmt.values.map(
@@ -12246,6 +12596,7 @@ function tryFastInsert(stmt, env) {
12246
12596
  } catch {
12247
12597
  return null;
12248
12598
  }
12599
+ if (env.state.databaseForTable(table).triggers.size > 0) return null;
12249
12600
  if (!table.isUnconstrained()) return null;
12250
12601
  const values = /* @__PURE__ */ new Map();
12251
12602
  for (const slot of plan.slots) values.set(slot.key, applyAffinity(slot.read(env), slot.affinity));
@@ -12290,9 +12641,20 @@ function buildFastInsertPlan(stmt, env) {
12290
12641
  return { tableName: stmt.table, slots };
12291
12642
  }
12292
12643
  function executeUpdate(stmt, env) {
12644
+ try {
12645
+ return withDmlCtes(stmt.with, env, () => executeUpdateCore(stmt, env));
12646
+ } catch (error) {
12647
+ handleConflictRollback(stmt.or === "rollback", error, env);
12648
+ throw error;
12649
+ }
12650
+ }
12651
+ function executeUpdateCore(stmt, env) {
12293
12652
  if (env.state.isVirtualTable(stmt.table)) {
12294
12653
  return executeVirtualUpdate(stmt, env);
12295
12654
  }
12655
+ const totalBefore = env.state.totalChanges;
12656
+ const view = writableView(stmt.table, "UPDATE", env);
12657
+ if (view) return executeViewUpdate(stmt, view, env, totalBefore);
12296
12658
  const table = env.state.getWritableTable(stmt.table);
12297
12659
  const alias = stmt.alias ?? stmt.table;
12298
12660
  const candidates = [];
@@ -12345,13 +12707,24 @@ function executeUpdate(stmt, env) {
12345
12707
  throw error;
12346
12708
  }
12347
12709
  }
12348
- env.state.recordChange(changes);
12349
- return valuesToResult(returningNames(stmt.returning, table), returningRows, changes, env.state.lastInsertRowid);
12710
+ const reportedChanges = finalizeDmlChanges(totalBefore, changes, env);
12711
+ return valuesToResult(
12712
+ returningNames(stmt.returning, table),
12713
+ returningRows,
12714
+ reportedChanges,
12715
+ env.state.lastInsertRowid
12716
+ );
12350
12717
  }
12351
12718
  function executeDelete(stmt, env) {
12719
+ return withDmlCtes(stmt.with, env, () => executeDeleteCore(stmt, env));
12720
+ }
12721
+ function executeDeleteCore(stmt, env) {
12352
12722
  if (env.state.isVirtualTable(stmt.table)) {
12353
12723
  return executeVirtualDelete(stmt, env);
12354
12724
  }
12725
+ const totalBefore = env.state.totalChanges;
12726
+ const view = writableView(stmt.table, "DELETE", env);
12727
+ if (view) return executeViewDelete(stmt, view, env, totalBefore);
12355
12728
  const table = env.state.getWritableTable(stmt.table);
12356
12729
  const selectedSource = stmt.where === null ? null : tryIndexedTableRows({ type: "table", schema: null, name: stmt.table, alias: stmt.alias }, stmt.where, env);
12357
12730
  const selected = [...selectedSource ? selectedSource.rows : table.scan()].filter((row) => {
@@ -12369,8 +12742,120 @@ function executeDelete(stmt, env) {
12369
12742
  fireDeleteTriggers("AFTER", table, row, env);
12370
12743
  changes++;
12371
12744
  }
12372
- env.state.recordChange(changes);
12373
- return valuesToResult(returningNames(stmt.returning, table), returningRows, changes, env.state.lastInsertRowid);
12745
+ const reportedChanges = finalizeDmlChanges(totalBefore, changes, env);
12746
+ return valuesToResult(
12747
+ returningNames(stmt.returning, table),
12748
+ returningRows,
12749
+ reportedChanges,
12750
+ env.state.lastInsertRowid
12751
+ );
12752
+ }
12753
+ function withDmlCtes(withClause, env, execute) {
12754
+ if (!withClause) return execute();
12755
+ const savedCtes = new Map(env.ctes);
12756
+ try {
12757
+ executeWith(withClause, env);
12758
+ return execute();
12759
+ } finally {
12760
+ env.ctes.clear();
12761
+ for (const [name, result] of savedCtes) env.ctes.set(name, result);
12762
+ }
12763
+ }
12764
+ function handleConflictRollback(rollback, error, env) {
12765
+ if (rollback && env.transactions.inTransaction && error instanceof SqliteError && error.category.startsWith("constraint")) {
12766
+ env.transactions.rollback();
12767
+ }
12768
+ }
12769
+ function writableView(name, event, env) {
12770
+ const { schema, bare } = splitQualifiedName(name);
12771
+ const db = env.state.databaseForSchema(schema, name);
12772
+ const view = db.views.get(bare.toLowerCase());
12773
+ if (!view) return null;
12774
+ const hasInsteadOf = [...db.triggers.values()].some(
12775
+ (trigger) => trigger.tableName.toLowerCase() === bare.toLowerCase() && trigger.event === event && trigger.timing === "INSTEAD"
12776
+ );
12777
+ if (!hasInsteadOf) {
12778
+ env.state.getWritableTable(name);
12779
+ throw new SqliteError(`cannot modify ${bare} because it is a view`, "other");
12780
+ }
12781
+ const names = view.columns ?? executeSelect2(view.select, env).columns;
12782
+ return {
12783
+ schema,
12784
+ name: bare,
12785
+ view,
12786
+ table: new Table(
12787
+ bare,
12788
+ names.map((column) => makeColumnInfo(column, null))
12789
+ )
12790
+ };
12791
+ }
12792
+ function executeViewInsert(stmt, target, env, totalBefore) {
12793
+ const columnNames = stmt.columns ?? target.table.columns.map((column) => column.name);
12794
+ for (const name of columnNames) columnOf(target.table, name);
12795
+ const suppliedIndexes = target.table.columns.map(
12796
+ (column) => columnNames.findIndex((name) => name.toLowerCase() === normalizeColumnName(column.name))
12797
+ );
12798
+ for (const source of evaluateInsertSource(stmt, env)) {
12799
+ if (source.length !== columnNames.length) {
12800
+ throw new SqliteError(`${source.length} values for ${columnNames.length} columns`, "other");
12801
+ }
12802
+ const values = /* @__PURE__ */ new Map();
12803
+ target.table.columns.forEach((column, index) => {
12804
+ const supplied = suppliedIndexes[index] ?? -1;
12805
+ values.set(normalizeColumnName(column.name), supplied < 0 ? null : source[supplied] ?? null);
12806
+ });
12807
+ fireInsertTriggers("INSTEAD", target.table, values, null, env);
12808
+ }
12809
+ const changes = finalizeDmlChanges(totalBefore, 0, env);
12810
+ return emptyResult(changes, env.state.lastInsertRowid);
12811
+ }
12812
+ function executeViewUpdate(stmt, target, env, totalBefore) {
12813
+ const alias = stmt.alias ?? target.name;
12814
+ const scopes = scanView(target, alias, env);
12815
+ const updatedColumns = new Set(
12816
+ stmt.set.flatMap((item) => item.columns.map((name) => columnOf(target.table, name).name))
12817
+ );
12818
+ for (const scope of scopes) {
12819
+ const ctx = env.createEvalContext(scope);
12820
+ if (stmt.where && isTruthySql(evalExpr(stmt.where, ctx)) !== true) continue;
12821
+ const oldRow = viewRow(target.table, scope);
12822
+ const updates = evaluateSet(stmt.set, target.table, ctx);
12823
+ const newValues = mergedValues(target.table, oldRow, updates);
12824
+ fireUpdateTriggers("INSTEAD", target.table, oldRow, newValues, updatedColumns, env);
12825
+ }
12826
+ const changes = finalizeDmlChanges(totalBefore, 0, env);
12827
+ return emptyResult(changes, env.state.lastInsertRowid);
12828
+ }
12829
+ function executeViewDelete(stmt, target, env, totalBefore) {
12830
+ const alias = stmt.alias ?? target.name;
12831
+ for (const scope of scanView(target, alias, env)) {
12832
+ if (stmt.where && isTruthySql(evalExpr(stmt.where, env.createEvalContext(scope))) !== true) continue;
12833
+ fireDeleteTriggers("INSTEAD", target.table, viewRow(target.table, scope), env);
12834
+ }
12835
+ const changes = finalizeDmlChanges(totalBefore, 0, env);
12836
+ return emptyResult(changes, env.state.lastInsertRowid);
12837
+ }
12838
+ function scanView(target, alias, env) {
12839
+ return scanFrom(
12840
+ { type: "table", schema: target.schema, name: target.name, alias: alias === target.name ? null : alias },
12841
+ env
12842
+ );
12843
+ }
12844
+ function viewRow(table, scope) {
12845
+ const values = /* @__PURE__ */ new Map();
12846
+ for (const column of table.columns) {
12847
+ const key = normalizeColumnName(column.name);
12848
+ const cell = scope.cells.find((candidate) => normalizeColumnName(candidate.name) === key);
12849
+ values.set(key, cell?.value ?? null);
12850
+ }
12851
+ return { rowid: scope.rowid ?? 0, values };
12852
+ }
12853
+ function finalizeDmlChanges(totalBefore, directChanges, env, last) {
12854
+ const triggerChanges = env.state.totalChanges - totalBefore;
12855
+ env.state.recordChange(directChanges, last);
12856
+ const reportedChanges = triggerChanges + directChanges;
12857
+ env.state.changes = reportedChanges;
12858
+ return reportedChanges;
12374
12859
  }
12375
12860
  function mergedValues(table, row, updates) {
12376
12861
  const values = /* @__PURE__ */ new Map();
@@ -12681,7 +13166,7 @@ function applyReferentialDelete(parent, row, env) {
12681
13166
  } else if (constraint.onDelete === "SET DEFAULT") {
12682
13167
  const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
12683
13168
  changes += 1 + updated.cascaded;
12684
- } else if (!fkIsDeferred(constraint, env)) {
13169
+ } else if (constraint.onDelete === "RESTRICT" || !fkIsDeferred(constraint, env)) {
12685
13170
  throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
12686
13171
  }
12687
13172
  }
@@ -12724,7 +13209,7 @@ function applyReferentialUpdate(parent, before, after, env) {
12724
13209
  } else if (constraint.onUpdate === "SET DEFAULT") {
12725
13210
  const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
12726
13211
  changes += 1 + updated.cascaded;
12727
- } else if (!fkIsDeferred(constraint, env)) {
13212
+ } else if (constraint.onUpdate === "RESTRICT" || !fkIsDeferred(constraint, env)) {
12728
13213
  throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
12729
13214
  }
12730
13215
  }
@@ -13005,6 +13490,11 @@ function executePragma(name, expr, env) {
13005
13490
  if (!env.transactions.inTransaction) env.state.foreignKeysEnabled = coercePragmaTruthy(value);
13006
13491
  return emptyResult(0, env.state.lastInsertRowid);
13007
13492
  }
13493
+ if (key === "case_sensitive_like" && expr !== null) {
13494
+ const value = evalPragmaSetValue(expr, env);
13495
+ env.state.caseSensitiveLike = coercePragmaTruthy(value);
13496
+ return emptyResult(0, env.state.lastInsertRowid);
13497
+ }
13008
13498
  if ((key === "user_version" || key === "schema_version") && expr !== null) {
13009
13499
  const value = evalPragmaSetValue(expr, env);
13010
13500
  const num2 = coercePragmaInt(value);
@@ -13217,7 +13707,10 @@ var Statement = class _Statement {
13217
13707
  execute(params, options) {
13218
13708
  this.database.assertOpen();
13219
13709
  this.reprepareIfSchemaChanged();
13220
- if (this.statements.length === 0) throw new SqliteError("empty statement", "misuse");
13710
+ if (this.statements.length === 0) {
13711
+ if (options?.named) throw new SqliteError("empty statement", "misuse");
13712
+ return emptyResult(this.database.state.changes, this.database.state.lastInsertRowid);
13713
+ }
13221
13714
  this.namedPlan ??= planNamedParameters(this.sql);
13222
13715
  const expected = this.namedPlan.expectedCount;
13223
13716
  if (params.length > 0 && params.length !== expected) {
@@ -13289,11 +13782,18 @@ function planNamedParameters(sql) {
13289
13782
  var Database = class {
13290
13783
  /** @internal Engine catalog, tables, and mutation counters. */
13291
13784
  state = new DatabaseState();
13292
- /** Seed used to construct the PRNG. */
13785
+ /** Seed used to construct the PRNG. Ignored when {@link randomMode} is `"os"`. */
13293
13786
  seed;
13787
+ /** Entropy mode for `random()` / `randomblob()`. */
13788
+ randomMode;
13789
+ /**
13790
+ * When true, `'now'` follows the wall clock and {@link restore} does not freeze it.
13791
+ * @internal
13792
+ */
13793
+ systemClock;
13294
13794
  /**
13295
13795
  * PRNG backing `random()` / `randomblob()` and related builtins.
13296
- * Prefer passing `seed` to the constructor.
13796
+ * Prefer passing `seed` / `random` to the constructor.
13297
13797
  * @internal
13298
13798
  */
13299
13799
  prng;
@@ -13316,7 +13816,9 @@ var Database = class {
13316
13816
  */
13317
13817
  constructor(options = {}) {
13318
13818
  this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
13319
- this.prng = new Prng(this.seed);
13819
+ this.randomMode = options.random ?? "deterministic";
13820
+ this.systemClock = options.now === "system";
13821
+ this.prng = this.randomMode === "os" ? new OsEntropy() : new Prng(this.seed);
13320
13822
  this.now = resolveClock(options.now);
13321
13823
  this.transactions = new TransactionManager(this.state, this.prng);
13322
13824
  }
@@ -13430,7 +13932,7 @@ var Database = class {
13430
13932
  this.state.replaceWith(decoded.state, { adopt: true });
13431
13933
  if (decoded.runtime) {
13432
13934
  this.prng.setState(decoded.runtime.prngState);
13433
- this.now = fixedClock(new Date(decoded.runtime.nowMs));
13935
+ if (!this.systemClock) this.now = fixedClock(new Date(decoded.runtime.nowMs));
13434
13936
  }
13435
13937
  }
13436
13938
  /**
@@ -13465,6 +13967,15 @@ var Database = class {
13465
13967
  this.assertOpen();
13466
13968
  return this.state.lastInsertRowid;
13467
13969
  }
13970
+ /**
13971
+ * Cumulative rows changed by INSERT / UPDATE / DELETE (SQLite `total_changes()`).
13972
+ *
13973
+ * @throws {SqliteError} If the database is closed.
13974
+ */
13975
+ get totalChanges() {
13976
+ this.assertOpen();
13977
+ return this.state.totalChanges;
13978
+ }
13468
13979
  /**
13469
13980
  * Throw if {@link close} has already been called.
13470
13981
  * @internal