@crvouga/sqlite-mem 1.1.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/unstable.js CHANGED
@@ -125,6 +125,23 @@ function applyAffinity(value, affinity) {
125
125
  return value;
126
126
  }
127
127
  }
128
+ function applyComparisonAffinity(left, right, leftAffinity, rightAffinity) {
129
+ const leftNumeric = leftAffinity === "INTEGER" || leftAffinity === "REAL" || leftAffinity === "NUMERIC";
130
+ const rightNumeric = rightAffinity === "INTEGER" || rightAffinity === "REAL" || rightAffinity === "NUMERIC";
131
+ const leftNone = leftAffinity === null || leftAffinity === "BLOB";
132
+ const rightNone = rightAffinity === null || rightAffinity === "BLOB";
133
+ if (leftNumeric && (rightAffinity === "TEXT" || rightNone)) {
134
+ const affinity = leftAffinity === "REAL" ? "REAL" : "NUMERIC";
135
+ return [left, applyAffinity(right, affinity)];
136
+ }
137
+ if (rightNumeric && (leftAffinity === "TEXT" || leftNone)) {
138
+ const affinity = rightAffinity === "REAL" ? "REAL" : "NUMERIC";
139
+ return [applyAffinity(left, affinity), right];
140
+ }
141
+ if (leftAffinity === "TEXT" && rightNone) return [left, applyAffinity(right, "TEXT")];
142
+ if (rightAffinity === "TEXT" && leftNone) return [applyAffinity(left, "TEXT"), right];
143
+ return [left, right];
144
+ }
128
145
  function coerceToNumber(value) {
129
146
  if (value === null) return null;
130
147
  if (value instanceof SqlReal) return value.value;
@@ -281,14 +298,18 @@ function jsonErrorPosition(input) {
281
298
  }
282
299
  }
283
300
  function isValidJsonText(input, flags = 0) {
284
- const allowJson5 = (flags & 2) !== 0 || (flags & 1) !== 0;
301
+ const allowCanonical = flags === 0 || (flags & 1) !== 0;
302
+ const allowJson5 = (flags & 2) !== 0;
285
303
  try {
286
304
  if (allowJson5) {
287
305
  parseJsonText(input);
288
306
  return true;
289
307
  }
290
- parseJsonText(input, { strictCanonical: true });
291
- return true;
308
+ if (allowCanonical) {
309
+ parseJsonText(input, { strictCanonical: true });
310
+ return true;
311
+ }
312
+ return false;
292
313
  } catch {
293
314
  return false;
294
315
  }
@@ -1694,7 +1715,11 @@ function applyModifier(date, modifier) {
1694
1715
  const normalized = modifier.trim().toLowerCase();
1695
1716
  if (normalized === "unixepoch") return new Date((date.getTime() / 864e5 + JULIAN_UNIX_EPOCH) * 1e3);
1696
1717
  if (normalized === "utc" || normalized === "localtime") return result;
1697
- if (normalized === "start of day") result.setUTCHours(0, 0, 0, 0);
1718
+ const weekday = /^weekday\s+([0-6])$/.exec(normalized);
1719
+ if (weekday) {
1720
+ const target = Number(weekday[1]);
1721
+ result.setUTCDate(result.getUTCDate() + (target - result.getUTCDay() + 7) % 7);
1722
+ } else if (normalized === "start of day") result.setUTCHours(0, 0, 0, 0);
1698
1723
  else if (normalized === "start of month") {
1699
1724
  result.setUTCDate(1);
1700
1725
  result.setUTCHours(0, 0, 0, 0);
@@ -2152,13 +2177,15 @@ var mathFunctions = {
2152
2177
  function escapeRegexChar(char) {
2153
2178
  return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
2154
2179
  }
2155
- function likeLiteral(char) {
2156
- const code = char.codePointAt(0);
2157
- if (code >= 65 && code <= 90) return `[${char}${char.toLowerCase()}]`;
2158
- if (code >= 97 && code <= 122) return `[${char}${char.toUpperCase()}]`;
2180
+ function likeLiteral(char, caseSensitive) {
2181
+ if (!caseSensitive) {
2182
+ const code = char.codePointAt(0);
2183
+ if (code >= 65 && code <= 90) return `[${char}${char.toLowerCase()}]`;
2184
+ if (code >= 97 && code <= 122) return `[${char}${char.toUpperCase()}]`;
2185
+ }
2159
2186
  return escapeRegexChar(char);
2160
2187
  }
2161
- function likeMatch(text2, pattern, escape = null) {
2188
+ function likeMatch(text2, pattern, escape = null, caseSensitive = false) {
2162
2189
  if (escape !== null && [...escape].length !== 1) {
2163
2190
  throw new SqliteError("ESCAPE expression must be a single character", "other");
2164
2191
  }
@@ -2168,13 +2195,13 @@ function likeMatch(text2, pattern, escape = null) {
2168
2195
  const char = chars[i];
2169
2196
  if (escape !== null && char === escape) {
2170
2197
  const next = chars[++i];
2171
- source += next === void 0 ? likeLiteral(char) : likeLiteral(next);
2198
+ source += next === void 0 ? likeLiteral(char, caseSensitive) : likeLiteral(next, caseSensitive);
2172
2199
  } else if (char === "%") {
2173
2200
  source += "[\\s\\S]*";
2174
2201
  } else if (char === "_") {
2175
2202
  source += "[\\s\\S]";
2176
2203
  } else {
2177
- source += likeLiteral(char);
2204
+ source += likeLiteral(char, caseSensitive);
2178
2205
  }
2179
2206
  }
2180
2207
  return new RegExp(`${source}$`).test(text2);
@@ -2481,11 +2508,11 @@ var scalarFunctions = {
2481
2508
  requireArgs3("load_extension", args, 1, 2);
2482
2509
  throw new SqliteError("not authorized", "misuse");
2483
2510
  },
2484
- like(args) {
2511
+ like(args, context) {
2485
2512
  requireArgs3("like", args, 2, 3);
2486
2513
  if (args[0] === null || args[1] === null || args[2] === null) return null;
2487
2514
  const escape = args[2] === void 0 ? null : text(args[2]);
2488
- return likeMatch(text(args[1]), text(args[0]), escape) ? 1 : 0;
2515
+ return likeMatch(text(args[1]), text(args[0]), escape, context.caseSensitiveLike === true) ? 1 : 0;
2489
2516
  },
2490
2517
  glob(args) {
2491
2518
  requireArgs3("glob", args, 2);
@@ -2830,7 +2857,8 @@ function sqlOr(left, right) {
2830
2857
  if (leftTruth === null || rightTruth === null) return null;
2831
2858
  return 0;
2832
2859
  }
2833
- function compareResult(op, left, right, collation) {
2860
+ function compareResult(op, left, right, collation, leftAffinity = null, rightAffinity = null) {
2861
+ [left, right] = applyComparisonAffinity(left, right, leftAffinity, rightAffinity);
2834
2862
  if (op === "IS" || op === "IS NOT" || op === "IS DISTINCT FROM" || op === "IS NOT DISTINCT FROM") {
2835
2863
  const equal = left === null || right === null ? left === right : (collation ? compareWithCollation(left, right, collation) : compareSql(left, right)) === 0;
2836
2864
  if (op === "IS" || op === "IS NOT DISTINCT FROM") return booleanValue(equal);
@@ -2883,11 +2911,18 @@ function evalBinary(op, leftExpr, rightExpr, ctx) {
2883
2911
  if (["=", "==", "!=", "<>", "<", "<=", ">", ">=", "IS", "IS NOT", "IS DISTINCT FROM", "IS NOT DISTINCT FROM"].includes(
2884
2912
  op
2885
2913
  )) {
2886
- return compareResult(op, left, right, resolveComparisonCollation(leftExpr, rightExpr, ctx) ?? void 0);
2914
+ return compareResult(
2915
+ op,
2916
+ left,
2917
+ right,
2918
+ resolveComparisonCollation(leftExpr, rightExpr, ctx) ?? void 0,
2919
+ resolveComparisonAffinity(leftExpr, ctx),
2920
+ resolveComparisonAffinity(rightExpr, ctx)
2921
+ );
2887
2922
  }
2888
2923
  if (op === "LIKE" || op === "NOT LIKE" || op === "GLOB" || op === "NOT GLOB") {
2889
2924
  if (left === null || right === null) return null;
2890
- const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right)) : globMatch(textValue(left), textValue(right));
2925
+ const matches = op.includes("LIKE") ? likeMatch(textValue(left), textValue(right), null, ctx.functionContext?.caseSensitiveLike === true) : globMatch(textValue(left), textValue(right));
2891
2926
  return booleanValue(op.startsWith("NOT") ? !matches : matches);
2892
2927
  }
2893
2928
  if (left === null || right === null) return null;
@@ -3062,14 +3097,26 @@ function inheritedCollation(expr, ctx) {
3062
3097
  return null;
3063
3098
  }
3064
3099
  }
3065
- function evalIn(left, values, not) {
3100
+ function resolveComparisonAffinity(expr, ctx) {
3101
+ switch (expr.type) {
3102
+ case "column":
3103
+ return ctx.resolveAffinity?.(expr.table, expr.name) ?? ctx.parent?.resolveAffinity?.(expr.table, expr.name) ?? null;
3104
+ case "cast":
3105
+ return affinityFromTypeName(expr.typeName);
3106
+ case "collate":
3107
+ return resolveComparisonAffinity(expr.expr, ctx);
3108
+ default:
3109
+ return null;
3110
+ }
3111
+ }
3112
+ function evalIn(left, values, not, leftAffinity) {
3066
3113
  if (values.length === 0) return booleanValue(not);
3067
3114
  if (left === null) return null;
3068
3115
  let sawNull = false;
3069
3116
  for (const value of values) {
3070
3117
  if (value === null) {
3071
3118
  sawNull = true;
3072
- } else if (compareSql(left, value) === 0) {
3119
+ } else if (compareSql(...applyComparisonAffinity(left, value, leftAffinity, null)) === 0) {
3073
3120
  return booleanValue(!not);
3074
3121
  }
3075
3122
  }
@@ -3115,8 +3162,9 @@ function evalExpr(expr, ctx) {
3115
3162
  case "between": {
3116
3163
  const value = evalExpr(expr.expr, ctx);
3117
3164
  const collation = resolveComparisonCollation(expr.expr, expr.lower, ctx) ?? resolveComparisonCollation(expr.expr, expr.upper, ctx) ?? void 0;
3118
- const lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation);
3119
- const result = sqlAnd(lower, () => compareResult("<=", value, evalExpr(expr.upper, ctx), collation));
3165
+ const affinity = resolveComparisonAffinity(expr.expr, ctx);
3166
+ const lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation, affinity);
3167
+ const result = sqlAnd(lower, () => compareResult("<=", value, evalExpr(expr.upper, ctx), collation, affinity));
3120
3168
  if (result === null) return null;
3121
3169
  return expr.not ? booleanValue(result === 0) : result;
3122
3170
  }
@@ -3143,14 +3191,19 @@ function evalExpr(expr, ctx) {
3143
3191
  }
3144
3192
  const left = evalExpr(expr.expr, ctx);
3145
3193
  const values = Array.isArray(expr.values) ? expr.values.map((value) => evalExpr(value, ctx)) : executeSelect(ctx, expr.values).rows.map((row) => row[0] ?? null);
3146
- return evalIn(left, values, expr.not);
3194
+ return evalIn(left, values, expr.not, resolveComparisonAffinity(expr.expr, ctx));
3147
3195
  }
3148
3196
  case "like": {
3149
3197
  const value = evalExpr(expr.expr, ctx);
3150
3198
  const pattern = evalExpr(expr.pattern, ctx);
3151
3199
  const escape = expr.escape === null ? null : evalExpr(expr.escape, ctx);
3152
3200
  if (value === null || pattern === null || escape === null && expr.escape !== null) return null;
3153
- const match = expr.op === "LIKE" ? likeMatch(textValue(value), textValue(pattern), escape === null ? null : textValue(escape)) : globMatch(textValue(value), textValue(pattern));
3201
+ const match = expr.op === "LIKE" ? likeMatch(
3202
+ textValue(value),
3203
+ textValue(pattern),
3204
+ escape === null ? null : textValue(escape),
3205
+ ctx.functionContext?.caseSensitiveLike === true
3206
+ ) : globMatch(textValue(value), textValue(pattern));
3154
3207
  return booleanValue(expr.not ? !match : match);
3155
3208
  }
3156
3209
  case "case": {
@@ -3837,10 +3890,12 @@ var PREC = {
3837
3890
  JSON_ARROW: 80
3838
3891
  };
3839
3892
  var Parser = class {
3840
- constructor(tokens) {
3893
+ constructor(tokens, source = "") {
3841
3894
  this.tokens = tokens;
3895
+ this.source = source;
3842
3896
  }
3843
3897
  tokens;
3898
+ source;
3844
3899
  pos = 0;
3845
3900
  current() {
3846
3901
  return this.tokens[this.pos] ?? this.tokens[this.tokens.length - 1];
@@ -3924,13 +3979,21 @@ var Parser = class {
3924
3979
  }
3925
3980
  // ── Statements ──────────────────────────────────────────────────────────
3926
3981
  parseStatements() {
3927
- const stmts = [];
3982
+ return this.parseUnits().map((unit) => unit.statement);
3983
+ }
3984
+ /** Parse statements with per-statement source slices for catalog `sql` text. */
3985
+ parseUnits() {
3986
+ const units = [];
3928
3987
  while (!this.at("EOF")) {
3929
3988
  if (this.match("SEMI")) continue;
3930
- stmts.push(this.parseStatement());
3989
+ const start = this.current().start;
3990
+ const statement = this.parseStatement();
3991
+ const end = this.pos > 0 ? this.tokens[this.pos - 1].end : this.current().end;
3931
3992
  this.match("SEMI");
3993
+ const sql = this.source ? this.source.slice(start, end).trimEnd() : "";
3994
+ units.push({ statement, sql });
3932
3995
  }
3933
- return stmts;
3996
+ return units;
3934
3997
  }
3935
3998
  parseStatement() {
3936
3999
  if (this.match("EXPLAIN")) {
@@ -3942,13 +4005,20 @@ var Parser = class {
3942
4005
  const stmt = this.parseStatement();
3943
4006
  return { type: "explain", queryPlan, statement: stmt };
3944
4007
  }
3945
- if (this.check("WITH", "SELECT")) return this.parseSelectStmt();
4008
+ if (this.at("WITH")) {
4009
+ const withClause = this.parseWithClause();
4010
+ if (this.at("SELECT")) return this.parseSelectStmt(withClause);
4011
+ if (this.at("INSERT") || this.at("REPLACE")) return this.parseInsertStmt(withClause);
4012
+ if (this.at("UPDATE")) return this.parseUpdateStmt(withClause);
4013
+ if (this.at("DELETE")) return this.parseDeleteStmt(withClause);
4014
+ this.syntaxError("expected SELECT, INSERT, UPDATE, or DELETE after WITH clause");
4015
+ }
3946
4016
  if (this.at("SELECT")) return this.parseSelectStmt();
3947
- if (this.check("WITH", "INSERT") || this.at("INSERT") || this.at("REPLACE")) {
4017
+ if (this.at("INSERT") || this.at("REPLACE")) {
3948
4018
  return this.parseInsertStmt();
3949
4019
  }
3950
- if (this.check("WITH", "UPDATE") || this.at("UPDATE")) return this.parseUpdateStmt();
3951
- if (this.check("WITH", "DELETE") || this.at("DELETE")) return this.parseDeleteStmt();
4020
+ if (this.at("UPDATE")) return this.parseUpdateStmt();
4021
+ if (this.at("DELETE")) return this.parseDeleteStmt();
3952
4022
  if (this.at("CREATE")) return this.parseCreateStmt();
3953
4023
  if (this.at("DROP")) return this.parseDropStmt();
3954
4024
  if (this.at("ALTER")) return this.parseAlterTableStmt();
@@ -4010,12 +4080,15 @@ var Parser = class {
4010
4080
  this.expect("RPAREN");
4011
4081
  }
4012
4082
  this.expect("AS");
4013
- if (this.match("NOT")) this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
4014
- else this.match("MATERIALIZED");
4083
+ let materialized = null;
4084
+ if (this.match("NOT")) {
4085
+ this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
4086
+ materialized = "not_materialized";
4087
+ } else if (this.match("MATERIALIZED")) materialized = "materialized";
4015
4088
  this.expect("LPAREN");
4016
- const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.parseSelectCore();
4089
+ const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.at("WITH") ? this.parseSelectStmt() : this.parseSelectCore();
4017
4090
  this.expect("RPAREN");
4018
- ctes.push({ name, columns, select });
4091
+ ctes.push({ name, columns, materialized, select });
4019
4092
  } while (this.match("COMMA"));
4020
4093
  return { recursive, ctes };
4021
4094
  }
@@ -4023,8 +4096,7 @@ var Parser = class {
4023
4096
  return this.at("WITH") ? this.parseWithClause() : null;
4024
4097
  }
4025
4098
  // ── SELECT ──────────────────────────────────────────────────────────────
4026
- parseSelectStmt() {
4027
- const withClause = this.parseOptionalWith();
4099
+ parseSelectStmt(withClause = this.parseOptionalWith()) {
4028
4100
  const select = this.parseSelectCore();
4029
4101
  select.with = withClause;
4030
4102
  return select;
@@ -4322,8 +4394,7 @@ var Parser = class {
4322
4394
  return { limit: first, offset };
4323
4395
  }
4324
4396
  // ── INSERT / REPLACE ────────────────────────────────────────────────────
4325
- parseInsertStmt() {
4326
- const withClause = this.parseOptionalWith();
4397
+ parseInsertStmt(withClause = this.parseOptionalWith()) {
4327
4398
  let mode = "insert";
4328
4399
  if (this.match("REPLACE")) {
4329
4400
  mode = "replace";
@@ -4426,8 +4497,7 @@ var Parser = class {
4426
4497
  return this.parseResultColumns();
4427
4498
  }
4428
4499
  // ── UPDATE ──────────────────────────────────────────────────────────────
4429
- parseUpdateStmt() {
4430
- const withClause = this.parseOptionalWith();
4500
+ parseUpdateStmt(withClause = this.parseOptionalWith()) {
4431
4501
  this.expect("UPDATE");
4432
4502
  const or = this.mapUpdateOr(this.parseOrConflict());
4433
4503
  const table = this.parseTableName();
@@ -4442,8 +4512,7 @@ var Parser = class {
4442
4512
  return { type: "update", with: withClause, or, table, alias, set, from, where, returning };
4443
4513
  }
4444
4514
  // ── DELETE ──────────────────────────────────────────────────────────────
4445
- parseDeleteStmt() {
4446
- const withClause = this.parseOptionalWith();
4515
+ parseDeleteStmt(withClause = this.parseOptionalWith()) {
4447
4516
  this.expect("DELETE");
4448
4517
  this.expect("FROM");
4449
4518
  const table = this.parseTableName();
@@ -5112,6 +5181,24 @@ var Parser = class {
5112
5181
  left = this.parseLikeRhs(left, true, "GLOB");
5113
5182
  continue;
5114
5183
  }
5184
+ if (n.kind === "REGEXP") {
5185
+ this.advance();
5186
+ this.advance();
5187
+ const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
5188
+ left = {
5189
+ type: "unary",
5190
+ op: "NOT",
5191
+ expr: {
5192
+ type: "function",
5193
+ name: "REGEXP",
5194
+ distinct: false,
5195
+ args: [pattern, left],
5196
+ orderBy: [],
5197
+ filter: null
5198
+ }
5199
+ };
5200
+ continue;
5201
+ }
5115
5202
  if (n.kind === "BETWEEN") {
5116
5203
  this.advance();
5117
5204
  this.advance();
@@ -5167,6 +5254,12 @@ var Parser = class {
5167
5254
  left = this.parseLikeRhs(left, false, "GLOB");
5168
5255
  continue;
5169
5256
  }
5257
+ if (this.at("REGEXP") && PREC.IS_IN_LIKE >= minPrec) {
5258
+ this.advance();
5259
+ const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
5260
+ left = { type: "function", name: "REGEXP", distinct: false, args: [pattern, left], orderBy: [], filter: null };
5261
+ continue;
5262
+ }
5170
5263
  if (this.at("MATCH") && PREC.IS_IN_LIKE >= minPrec) {
5171
5264
  this.advance();
5172
5265
  const right2 = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
@@ -5325,7 +5418,7 @@ var Parser = class {
5325
5418
  }
5326
5419
  if (this.at("CURRENT_DATE") || this.at("CURRENT_TIME") || this.at("CURRENT_TIMESTAMP")) {
5327
5420
  const tok = this.advance();
5328
- return { type: "function", name: tok.value, distinct: false, args: [], filter: null };
5421
+ return { type: "function", name: tok.value, distinct: false, args: [], orderBy: [], filter: null };
5329
5422
  }
5330
5423
  if (this.at("NUMBER")) {
5331
5424
  const tok = this.advance();
@@ -5408,6 +5501,7 @@ var Parser = class {
5408
5501
  } while (this.match("COMMA"));
5409
5502
  }
5410
5503
  }
5504
+ const orderBy = this.parseOrderBy();
5411
5505
  this.expect("RPAREN");
5412
5506
  if (this.match("FILTER")) {
5413
5507
  this.expect("LPAREN");
@@ -5428,13 +5522,13 @@ var Parser = class {
5428
5522
  window = this.parseWindowSpec();
5429
5523
  this.expect("RPAREN");
5430
5524
  }
5431
- const func = isAgg ? { type: "aggregate", name: upper, distinct, args, filter } : { type: "function", name, distinct, args, filter };
5525
+ const func = isAgg ? { type: "aggregate", name: upper, distinct, args, orderBy, filter } : { type: "function", name, distinct, args, orderBy, filter };
5432
5526
  return { type: "window", func, window };
5433
5527
  }
5434
5528
  if (isAgg) {
5435
- return { type: "aggregate", name: upper, distinct, args, filter };
5529
+ return { type: "aggregate", name: upper, distinct, args, orderBy, filter };
5436
5530
  }
5437
- return { type: "function", name, distinct, args, filter };
5531
+ return { type: "function", name, distinct, args, orderBy, filter };
5438
5532
  }
5439
5533
  parseCaseExpr(base) {
5440
5534
  const whens = [];
@@ -5525,14 +5619,16 @@ var Parser = class {
5525
5619
  return { kind: "following", expr };
5526
5620
  }
5527
5621
  };
5528
- function parseTokens(tokens) {
5529
- return new Parser(tokens).parseStatements();
5622
+ function parseTokenUnits(tokens, source) {
5623
+ return new Parser(tokens, source).parseUnits();
5530
5624
  }
5531
5625
 
5532
5626
  // src/parser/index.ts
5533
5627
  function parse(sql) {
5534
- const tokens = tokenize(sql);
5535
- return parseTokens(tokens);
5628
+ return parseUnits(sql).map((unit) => unit.statement);
5629
+ }
5630
+ function parseUnits(sql) {
5631
+ return parseTokenUnits(tokenize(sql), sql);
5536
5632
  }
5537
5633
 
5538
5634
  // src/runtime/clock.ts
@@ -5542,8 +5638,12 @@ function fixedClock(instant = DEFAULT_NOW) {
5542
5638
  if (Number.isNaN(ms)) throw new RangeError("invalid clock instant");
5543
5639
  return () => new Date(ms);
5544
5640
  }
5641
+ function systemClock() {
5642
+ return () => /* @__PURE__ */ new Date();
5643
+ }
5545
5644
  function resolveClock(now) {
5546
5645
  if (now === void 0) return fixedClock(DEFAULT_NOW);
5646
+ if (now === "system") return systemClock();
5547
5647
  if (typeof now === "function") return () => new Date(now().getTime());
5548
5648
  return fixedClock(now);
5549
5649
  }
@@ -5608,6 +5708,28 @@ var Prng = class _Prng {
5608
5708
  return copy;
5609
5709
  }
5610
5710
  };
5711
+ var OsEntropy = class _OsEntropy extends Prng {
5712
+ constructor() {
5713
+ super(1);
5714
+ }
5715
+ nextU64() {
5716
+ const bytes = new Uint8Array(8);
5717
+ crypto.getRandomValues(bytes);
5718
+ let value = 0n;
5719
+ for (let i = 0; i < 8; i++) {
5720
+ value |= BigInt(bytes[i]) << BigInt(i * 8);
5721
+ }
5722
+ return BigInt.asUintN(64, value);
5723
+ }
5724
+ getState() {
5725
+ return 0n;
5726
+ }
5727
+ setState(_state) {
5728
+ }
5729
+ clone() {
5730
+ return new _OsEntropy();
5731
+ }
5732
+ };
5611
5733
  function deriveSeed(...parts) {
5612
5734
  let hash = 2166136261;
5613
5735
  for (const part of parts) {
@@ -6683,7 +6805,7 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6683
6805
  insert(values, rowid) {
6684
6806
  const command = this.detectCommand(values);
6685
6807
  if (command !== null) {
6686
- this.runCommand(command, values);
6808
+ this.runCommand(command, values, rowid);
6687
6809
  return 0;
6688
6810
  }
6689
6811
  const assigned = rowid ?? this.nextRowid++;
@@ -6832,18 +6954,63 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6832
6954
  }
6833
6955
  return parts.join(" ");
6834
6956
  }
6835
- /** FTS3 matchinfo default format 'pcx' simplified blob of 32-bit LE ints. */
6836
- matchinfo(cursor, _format = "pcx") {
6957
+ /** FTS3 matchinfo format encoded as native-style 32-bit little-endian integers. */
6958
+ matchinfo(cursor, format = "pcx") {
6837
6959
  const row = this.rows.get(cursor.rowid);
6838
6960
  const nPhrase = Math.max(1, cursor.phraseTerms.length);
6839
6961
  const nCol = this.columns.length;
6840
- const values = [nPhrase, nCol];
6841
- for (let p = 0; p < nPhrase; p++) {
6842
- for (let c = 0; c < nCol; c++) {
6843
- const col = this.columns[c];
6844
- const term = cursor.phraseTerms[p]?.[0] ?? "";
6845
- const tf = row ? this.termFreq(row, col, term) : 0;
6846
- values.push(tf);
6962
+ const values = [];
6963
+ for (const request of format) {
6964
+ switch (request) {
6965
+ case "p":
6966
+ values.push(nPhrase);
6967
+ break;
6968
+ case "c":
6969
+ values.push(nCol);
6970
+ break;
6971
+ case "s":
6972
+ for (let c = 0; c < nCol; c++) {
6973
+ let longest = 0;
6974
+ let run = 0;
6975
+ for (let p = 0; p < nPhrase; p++) {
6976
+ const phrase = cursor.phraseTerms[p] ?? [];
6977
+ if (row && this.phraseFreq(row, this.columns[c], phrase) > 0) {
6978
+ run++;
6979
+ longest = Math.max(longest, run);
6980
+ } else {
6981
+ run = 0;
6982
+ }
6983
+ }
6984
+ values.push(longest);
6985
+ }
6986
+ break;
6987
+ case "x":
6988
+ for (let p = 0; p < nPhrase; p++) {
6989
+ const phrase = cursor.phraseTerms[p] ?? [];
6990
+ for (let c = 0; c < nCol; c++) {
6991
+ const column = this.columns[c];
6992
+ const localHits = row ? this.phraseFreq(row, column, phrase) : 0;
6993
+ let globalHits = 0;
6994
+ let matchingRows = 0;
6995
+ for (const candidate of this.rows.values()) {
6996
+ const hits = this.phraseFreq(candidate, column, phrase);
6997
+ globalHits += hits;
6998
+ if (hits > 0) matchingRows++;
6999
+ }
7000
+ values.push(localHits, globalHits, matchingRows);
7001
+ }
7002
+ }
7003
+ break;
7004
+ case "y":
7005
+ for (let p = 0; p < nPhrase; p++) {
7006
+ const phrase = cursor.phraseTerms[p] ?? [];
7007
+ for (let c = 0; c < nCol; c++) {
7008
+ values.push(row ? this.phraseFreq(row, this.columns[c], phrase) : 0);
7009
+ }
7010
+ }
7011
+ break;
7012
+ default:
7013
+ throw new SqliteError(`unrecognized matchinfo request: ${request}`, "other");
6847
7014
  }
6848
7015
  }
6849
7016
  const buf = new Uint8Array(values.length * 4);
@@ -6891,13 +7058,9 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6891
7058
  if (!values.has(key)) return null;
6892
7059
  const v = values.get(key);
6893
7060
  if (typeof v !== "string") return null;
6894
- const otherContent = [...values.entries()].some(
6895
- ([k, val]) => k !== key && val !== null && this.columns.some((c) => c.toLowerCase() === k)
6896
- );
6897
- if (otherContent) return null;
6898
7061
  return v;
6899
7062
  }
6900
- runCommand(command, _values) {
7063
+ runCommand(command, _values, rowid) {
6901
7064
  const cmd = command.toLowerCase();
6902
7065
  if (cmd === "optimize") return;
6903
7066
  if (cmd === "rebuild") {
@@ -6912,13 +7075,15 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6912
7075
  "other"
6913
7076
  );
6914
7077
  }
6915
- for (const rowid of [...this.rows.keys()]) this.delete(rowid);
7078
+ for (const rowid2 of [...this.rows.keys()]) this.delete(rowid2);
6916
7079
  return;
6917
7080
  }
6918
7081
  if (cmd.startsWith("merge=") || cmd.startsWith("automerge=")) {
6919
7082
  throw new SqliteError("SQL logic error", "other");
6920
7083
  }
6921
7084
  if (cmd === "delete") {
7085
+ if (rowid === void 0) throw new SqliteError("SQL logic error", "other");
7086
+ this.delete(rowid);
6922
7087
  return;
6923
7088
  }
6924
7089
  throw new SqliteError("SQL logic error", "other");
@@ -7151,11 +7316,6 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
7151
7316
  }
7152
7317
  return n;
7153
7318
  }
7154
- termFreq(row, column, term) {
7155
- const needle = this.normalizeQueryTerm(term);
7156
- const tokens = row.tokensByColumn.get(column.toLowerCase()) ?? [];
7157
- return tokens.filter((t) => t.term === needle || t.term.startsWith(needle)).length;
7158
- }
7159
7319
  docLength(row) {
7160
7320
  let n = 0;
7161
7321
  for (const col of this.indexedColumns()) n += (row.tokensByColumn.get(col.toLowerCase()) ?? []).length;
@@ -7419,6 +7579,8 @@ var Table = class _Table {
7419
7579
  scanCache = null;
7420
7580
  /** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
7421
7581
  equalityHashes = null;
7582
+ /** Cached maximum rowid. `undefined` means recompute after deleting the maximum. */
7583
+ maximumRowid = null;
7422
7584
  frozen = false;
7423
7585
  constructor(name, columns, options = {}) {
7424
7586
  this.name = name;
@@ -7621,7 +7783,12 @@ var Table = class _Table {
7621
7783
  if (alias) values.set(normalizeColumnName(alias.name), targetKey);
7622
7784
  const candidate = { rowid: targetKey, values };
7623
7785
  this.validate(candidate, key);
7624
- if (targetKey !== key) this.rows.delete(key);
7786
+ if (targetKey !== key) {
7787
+ this.rows.delete(key);
7788
+ if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
7789
+ this.maximumRowid = void 0;
7790
+ }
7791
+ }
7625
7792
  this.rows.set(targetKey, candidate);
7626
7793
  this.advanceNextRowid(targetKey);
7627
7794
  this.reindexEquality(existing, candidate);
@@ -7635,6 +7802,9 @@ var Table = class _Table {
7635
7802
  if (this.withoutRowid) this.clusteredRows.delete(this.makeClusterKey(existing.values));
7636
7803
  this.unindexEquality(existing);
7637
7804
  this.invalidateScan();
7805
+ if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
7806
+ this.maximumRowid = void 0;
7807
+ }
7638
7808
  return this.rows.delete(key);
7639
7809
  }
7640
7810
  *scan() {
@@ -7656,6 +7826,7 @@ var Table = class _Table {
7656
7826
  strict: this.strict
7657
7827
  });
7658
7828
  copy.nextRowid = this.nextRowid;
7829
+ copy.maximumRowid = this.maximumRowid;
7659
7830
  for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
7660
7831
  for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
7661
7832
  return copy;
@@ -7668,6 +7839,7 @@ var Table = class _Table {
7668
7839
  }
7669
7840
  /** Rebuild clustered storage after snapshot decode or bulk load. */
7670
7841
  rebuildClusteredRows() {
7842
+ this.maximumRowid = void 0;
7671
7843
  if (!this.withoutRowid) return;
7672
7844
  this.clusteredRows.clear();
7673
7845
  for (const row of this.rows.values()) {
@@ -7776,11 +7948,23 @@ var Table = class _Table {
7776
7948
  return primary[0];
7777
7949
  }
7778
7950
  allocateRowid() {
7951
+ if (!this.columns.some((column) => column.autoincrement)) {
7952
+ if (this.maximumRowid === void 0) {
7953
+ this.maximumRowid = null;
7954
+ for (const rowid of this.rows.keys()) {
7955
+ if (this.maximumRowid === null || compareRowids(rowid, this.maximumRowid) > 0) this.maximumRowid = rowid;
7956
+ }
7957
+ }
7958
+ return this.maximumRowid === null ? 1 : incrementRowid(this.maximumRowid);
7959
+ }
7779
7960
  let candidate = canonicalRowid(this.nextRowid);
7780
7961
  while (this.rows.has(candidate)) candidate = incrementRowid(candidate);
7781
7962
  return candidate;
7782
7963
  }
7783
7964
  advanceNextRowid(rowid) {
7965
+ if (this.maximumRowid === null || this.maximumRowid !== void 0 && compareRowids(rowid, this.maximumRowid) > 0) {
7966
+ this.maximumRowid = rowid;
7967
+ }
7784
7968
  if (compareRowids(rowid, this.nextRowid) >= 0) this.nextRowid = incrementRowid(rowid);
7785
7969
  }
7786
7970
  };
@@ -7855,6 +8039,8 @@ var DatabaseState = class _DatabaseState {
7855
8039
  changes = 0;
7856
8040
  totalChanges = 0;
7857
8041
  foreignKeysEnabled = false;
8042
+ /** When true, LIKE / like() are case-sensitive (SQLite `PRAGMA case_sensitive_like`). */
8043
+ caseSensitiveLike = false;
7858
8044
  schemaVersion = 0;
7859
8045
  userVersion = 0;
7860
8046
  databaseForSchema(schema, qualifiedForError) {
@@ -8299,6 +8485,7 @@ var DatabaseState = class _DatabaseState {
8299
8485
  copy.changes = this.changes;
8300
8486
  copy.totalChanges = this.totalChanges;
8301
8487
  copy.foreignKeysEnabled = this.foreignKeysEnabled;
8488
+ copy.caseSensitiveLike = this.caseSensitiveLike;
8302
8489
  copy.schemaVersion = this.schemaVersion;
8303
8490
  copy.userVersion = this.userVersion;
8304
8491
  return copy;
@@ -8318,6 +8505,7 @@ var DatabaseState = class _DatabaseState {
8318
8505
  copy.changes = this.changes;
8319
8506
  copy.totalChanges = this.totalChanges;
8320
8507
  copy.foreignKeysEnabled = this.foreignKeysEnabled;
8508
+ copy.caseSensitiveLike = this.caseSensitiveLike;
8321
8509
  copy.schemaVersion = this.schemaVersion;
8322
8510
  copy.userVersion = this.userVersion;
8323
8511
  return copy;
@@ -8379,6 +8567,7 @@ var DatabaseState = class _DatabaseState {
8379
8567
  this.changes = copy.changes;
8380
8568
  this.totalChanges = copy.totalChanges;
8381
8569
  this.foreignKeysEnabled = copy.foreignKeysEnabled;
8570
+ this.caseSensitiveLike = copy.caseSensitiveLike;
8382
8571
  this.schemaVersion = copy.schemaVersion;
8383
8572
  this.userVersion = copy.userVersion;
8384
8573
  }
@@ -8723,6 +8912,7 @@ function jsonReviver(_key, value) {
8723
8912
  export {
8724
8913
  DEFAULT_DATABASE_SEED,
8725
8914
  DEFAULT_NOW,
8915
+ OsEntropy,
8726
8916
  Prng,
8727
8917
  SqlJsonText,
8728
8918
  SqlReal,
@@ -8748,6 +8938,7 @@ export {
8748
8938
  resolveClock,
8749
8939
  sqlValueEquals,
8750
8940
  storageClassOf,
8941
+ systemClock,
8751
8942
  toInteger,
8752
8943
  tokenize,
8753
8944
  typeofSql,