@crvouga/sqlite-mem 1.1.2 → 1.2.0

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