@crvouga/sqlite-mem 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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;
@@ -3024,6 +3059,8 @@ function explicitCollation(expr) {
3024
3059
  case "unary":
3025
3060
  case "cast":
3026
3061
  return explicitCollation(expr.expr);
3062
+ case "is_bool":
3063
+ return explicitCollation(expr.expr);
3027
3064
  case "binary":
3028
3065
  return explicitCollation(expr.left) ?? explicitCollation(expr.right);
3029
3066
  case "between":
@@ -3054,18 +3091,32 @@ function inheritedCollation(expr, ctx) {
3054
3091
  case "unary":
3055
3092
  case "cast":
3056
3093
  return inheritedCollation(expr.expr, ctx);
3094
+ case "is_bool":
3095
+ return inheritedCollation(expr.expr, ctx);
3057
3096
  default:
3058
3097
  return null;
3059
3098
  }
3060
3099
  }
3061
- 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) {
3062
3113
  if (values.length === 0) return booleanValue(not);
3063
3114
  if (left === null) return null;
3064
3115
  let sawNull = false;
3065
3116
  for (const value of values) {
3066
3117
  if (value === null) {
3067
3118
  sawNull = true;
3068
- } else if (compareSql(left, value) === 0) {
3119
+ } else if (compareSql(...applyComparisonAffinity(left, value, leftAffinity, null)) === 0) {
3069
3120
  return booleanValue(!not);
3070
3121
  }
3071
3122
  }
@@ -3099,13 +3150,21 @@ function evalExpr(expr, ctx) {
3099
3150
  if (expr.op === "-") return asNumber(-numberValue(value));
3100
3151
  return ~integerValue(value);
3101
3152
  }
3153
+ case "is_bool": {
3154
+ const truth = isTruthySql(evalExpr(expr.expr, ctx));
3155
+ if (!expr.not && expr.sense) return booleanValue(truth === true);
3156
+ if (!expr.not && !expr.sense) return booleanValue(truth === false);
3157
+ if (expr.not && expr.sense) return booleanValue(truth !== true);
3158
+ return booleanValue(truth !== false);
3159
+ }
3102
3160
  case "binary":
3103
3161
  return evalBinary(expr.op, expr.left, expr.right, ctx);
3104
3162
  case "between": {
3105
3163
  const value = evalExpr(expr.expr, ctx);
3106
3164
  const collation = resolveComparisonCollation(expr.expr, expr.lower, ctx) ?? resolveComparisonCollation(expr.expr, expr.upper, ctx) ?? void 0;
3107
- const lower = compareResult(">=", value, evalExpr(expr.lower, ctx), collation);
3108
- 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));
3109
3168
  if (result === null) return null;
3110
3169
  return expr.not ? booleanValue(result === 0) : result;
3111
3170
  }
@@ -3132,14 +3191,19 @@ function evalExpr(expr, ctx) {
3132
3191
  }
3133
3192
  const left = evalExpr(expr.expr, ctx);
3134
3193
  const values = Array.isArray(expr.values) ? expr.values.map((value) => evalExpr(value, ctx)) : executeSelect(ctx, expr.values).rows.map((row) => row[0] ?? null);
3135
- return evalIn(left, values, expr.not);
3194
+ return evalIn(left, values, expr.not, resolveComparisonAffinity(expr.expr, ctx));
3136
3195
  }
3137
3196
  case "like": {
3138
3197
  const value = evalExpr(expr.expr, ctx);
3139
3198
  const pattern = evalExpr(expr.pattern, ctx);
3140
3199
  const escape = expr.escape === null ? null : evalExpr(expr.escape, ctx);
3141
3200
  if (value === null || pattern === null || escape === null && expr.escape !== null) return null;
3142
- 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));
3143
3207
  return booleanValue(expr.not ? !match : match);
3144
3208
  }
3145
3209
  case "case": {
@@ -3277,6 +3341,7 @@ var KEYWORDS = {
3277
3341
  FOREIGN: "FOREIGN",
3278
3342
  FROM: "FROM",
3279
3343
  FULL: "FULL",
3344
+ FALSE: "FALSE",
3280
3345
  GENERATED: "GENERATED",
3281
3346
  GLOB: "GLOB",
3282
3347
  GROUP: "GROUP",
@@ -3351,6 +3416,7 @@ var KEYWORDS = {
3351
3416
  TO: "TO",
3352
3417
  TRANSACTION: "TRANSACTION",
3353
3418
  TRIGGER: "TRIGGER",
3419
+ TRUE: "TRUE",
3354
3420
  UNBOUNDED: "UNBOUNDED",
3355
3421
  UNION: "UNION",
3356
3422
  UNIQUE: "UNIQUE",
@@ -3705,6 +3771,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
3705
3771
  "FOREIGN",
3706
3772
  "FROM",
3707
3773
  "FULL",
3774
+ "FALSE",
3708
3775
  "GENERATED",
3709
3776
  "GLOB",
3710
3777
  "GROUP",
@@ -3779,6 +3846,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
3779
3846
  "TO",
3780
3847
  "TRANSACTION",
3781
3848
  "TRIGGER",
3849
+ "TRUE",
3782
3850
  "UNBOUNDED",
3783
3851
  "UNION",
3784
3852
  "UNIQUE",
@@ -3927,13 +3995,20 @@ var Parser = class {
3927
3995
  const stmt = this.parseStatement();
3928
3996
  return { type: "explain", queryPlan, statement: stmt };
3929
3997
  }
3930
- if (this.check("WITH", "SELECT")) return this.parseSelectStmt();
3998
+ if (this.at("WITH")) {
3999
+ const withClause = this.parseWithClause();
4000
+ if (this.at("SELECT")) return this.parseSelectStmt(withClause);
4001
+ if (this.at("INSERT") || this.at("REPLACE")) return this.parseInsertStmt(withClause);
4002
+ if (this.at("UPDATE")) return this.parseUpdateStmt(withClause);
4003
+ if (this.at("DELETE")) return this.parseDeleteStmt(withClause);
4004
+ this.syntaxError("expected SELECT, INSERT, UPDATE, or DELETE after WITH clause");
4005
+ }
3931
4006
  if (this.at("SELECT")) return this.parseSelectStmt();
3932
- if (this.check("WITH", "INSERT") || this.at("INSERT") || this.at("REPLACE")) {
4007
+ if (this.at("INSERT") || this.at("REPLACE")) {
3933
4008
  return this.parseInsertStmt();
3934
4009
  }
3935
- if (this.check("WITH", "UPDATE") || this.at("UPDATE")) return this.parseUpdateStmt();
3936
- if (this.check("WITH", "DELETE") || this.at("DELETE")) return this.parseDeleteStmt();
4010
+ if (this.at("UPDATE")) return this.parseUpdateStmt();
4011
+ if (this.at("DELETE")) return this.parseDeleteStmt();
3937
4012
  if (this.at("CREATE")) return this.parseCreateStmt();
3938
4013
  if (this.at("DROP")) return this.parseDropStmt();
3939
4014
  if (this.at("ALTER")) return this.parseAlterTableStmt();
@@ -3995,12 +4070,15 @@ var Parser = class {
3995
4070
  this.expect("RPAREN");
3996
4071
  }
3997
4072
  this.expect("AS");
3998
- if (this.match("NOT")) this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
3999
- else this.match("MATERIALIZED");
4073
+ let materialized = null;
4074
+ if (this.match("NOT")) {
4075
+ this.expect("MATERIALIZED", "expected MATERIALIZED after NOT");
4076
+ materialized = "not_materialized";
4077
+ } else if (this.match("MATERIALIZED")) materialized = "materialized";
4000
4078
  this.expect("LPAREN");
4001
- const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.parseSelectCore();
4079
+ const select = this.at("VALUES") ? this.parseValuesAsSelect() : this.at("WITH") ? this.parseSelectStmt() : this.parseSelectCore();
4002
4080
  this.expect("RPAREN");
4003
- ctes.push({ name, columns, select });
4081
+ ctes.push({ name, columns, materialized, select });
4004
4082
  } while (this.match("COMMA"));
4005
4083
  return { recursive, ctes };
4006
4084
  }
@@ -4008,8 +4086,7 @@ var Parser = class {
4008
4086
  return this.at("WITH") ? this.parseWithClause() : null;
4009
4087
  }
4010
4088
  // ── SELECT ──────────────────────────────────────────────────────────────
4011
- parseSelectStmt() {
4012
- const withClause = this.parseOptionalWith();
4089
+ parseSelectStmt(withClause = this.parseOptionalWith()) {
4013
4090
  const select = this.parseSelectCore();
4014
4091
  select.with = withClause;
4015
4092
  return select;
@@ -4307,8 +4384,7 @@ var Parser = class {
4307
4384
  return { limit: first, offset };
4308
4385
  }
4309
4386
  // ── INSERT / REPLACE ────────────────────────────────────────────────────
4310
- parseInsertStmt() {
4311
- const withClause = this.parseOptionalWith();
4387
+ parseInsertStmt(withClause = this.parseOptionalWith()) {
4312
4388
  let mode = "insert";
4313
4389
  if (this.match("REPLACE")) {
4314
4390
  mode = "replace";
@@ -4411,8 +4487,7 @@ var Parser = class {
4411
4487
  return this.parseResultColumns();
4412
4488
  }
4413
4489
  // ── UPDATE ──────────────────────────────────────────────────────────────
4414
- parseUpdateStmt() {
4415
- const withClause = this.parseOptionalWith();
4490
+ parseUpdateStmt(withClause = this.parseOptionalWith()) {
4416
4491
  this.expect("UPDATE");
4417
4492
  const or = this.mapUpdateOr(this.parseOrConflict());
4418
4493
  const table = this.parseTableName();
@@ -4427,8 +4502,7 @@ var Parser = class {
4427
4502
  return { type: "update", with: withClause, or, table, alias, set, from, where, returning };
4428
4503
  }
4429
4504
  // ── DELETE ──────────────────────────────────────────────────────────────
4430
- parseDeleteStmt() {
4431
- const withClause = this.parseOptionalWith();
4505
+ parseDeleteStmt(withClause = this.parseOptionalWith()) {
4432
4506
  this.expect("DELETE");
4433
4507
  this.expect("FROM");
4434
4508
  const table = this.parseTableName();
@@ -5097,6 +5171,24 @@ var Parser = class {
5097
5171
  left = this.parseLikeRhs(left, true, "GLOB");
5098
5172
  continue;
5099
5173
  }
5174
+ if (n.kind === "REGEXP") {
5175
+ this.advance();
5176
+ this.advance();
5177
+ const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
5178
+ left = {
5179
+ type: "unary",
5180
+ op: "NOT",
5181
+ expr: {
5182
+ type: "function",
5183
+ name: "REGEXP",
5184
+ distinct: false,
5185
+ args: [pattern, left],
5186
+ orderBy: [],
5187
+ filter: null
5188
+ }
5189
+ };
5190
+ continue;
5191
+ }
5100
5192
  if (n.kind === "BETWEEN") {
5101
5193
  this.advance();
5102
5194
  this.advance();
@@ -5125,6 +5217,14 @@ var Parser = class {
5125
5217
  continue;
5126
5218
  }
5127
5219
  const not = this.match("NOT");
5220
+ if (this.match("TRUE")) {
5221
+ left = { type: "is_bool", expr: left, not, sense: true };
5222
+ continue;
5223
+ }
5224
+ if (this.match("FALSE")) {
5225
+ left = { type: "is_bool", expr: left, not, sense: false };
5226
+ continue;
5227
+ }
5128
5228
  const right2 = this.parseIsRhs();
5129
5229
  left = { type: "binary", op: not ? "IS NOT" : "IS", left, right: right2 };
5130
5230
  continue;
@@ -5144,6 +5244,12 @@ var Parser = class {
5144
5244
  left = this.parseLikeRhs(left, false, "GLOB");
5145
5245
  continue;
5146
5246
  }
5247
+ if (this.at("REGEXP") && PREC.IS_IN_LIKE >= minPrec) {
5248
+ this.advance();
5249
+ const pattern = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
5250
+ left = { type: "function", name: "REGEXP", distinct: false, args: [pattern, left], orderBy: [], filter: null };
5251
+ continue;
5252
+ }
5147
5253
  if (this.at("MATCH") && PREC.IS_IN_LIKE >= minPrec) {
5148
5254
  this.advance();
5149
5255
  const right2 = this.parseExprPrec(PREC.IS_IN_LIKE + 1);
@@ -5302,7 +5408,7 @@ var Parser = class {
5302
5408
  }
5303
5409
  if (this.at("CURRENT_DATE") || this.at("CURRENT_TIME") || this.at("CURRENT_TIMESTAMP")) {
5304
5410
  const tok = this.advance();
5305
- return { type: "function", name: tok.value, distinct: false, args: [], filter: null };
5411
+ return { type: "function", name: tok.value, distinct: false, args: [], orderBy: [], filter: null };
5306
5412
  }
5307
5413
  if (this.at("NUMBER")) {
5308
5414
  const tok = this.advance();
@@ -5385,6 +5491,7 @@ var Parser = class {
5385
5491
  } while (this.match("COMMA"));
5386
5492
  }
5387
5493
  }
5494
+ const orderBy = this.parseOrderBy();
5388
5495
  this.expect("RPAREN");
5389
5496
  if (this.match("FILTER")) {
5390
5497
  this.expect("LPAREN");
@@ -5405,13 +5512,13 @@ var Parser = class {
5405
5512
  window = this.parseWindowSpec();
5406
5513
  this.expect("RPAREN");
5407
5514
  }
5408
- const func = isAgg ? { type: "aggregate", name: upper, distinct, args, filter } : { type: "function", name, distinct, args, filter };
5515
+ const func = isAgg ? { type: "aggregate", name: upper, distinct, args, orderBy, filter } : { type: "function", name, distinct, args, orderBy, filter };
5409
5516
  return { type: "window", func, window };
5410
5517
  }
5411
5518
  if (isAgg) {
5412
- return { type: "aggregate", name: upper, distinct, args, filter };
5519
+ return { type: "aggregate", name: upper, distinct, args, orderBy, filter };
5413
5520
  }
5414
- return { type: "function", name, distinct, args, filter };
5521
+ return { type: "function", name, distinct, args, orderBy, filter };
5415
5522
  }
5416
5523
  parseCaseExpr(base) {
5417
5524
  const whens = [];
@@ -5519,8 +5626,12 @@ function fixedClock(instant = DEFAULT_NOW) {
5519
5626
  if (Number.isNaN(ms)) throw new RangeError("invalid clock instant");
5520
5627
  return () => new Date(ms);
5521
5628
  }
5629
+ function systemClock() {
5630
+ return () => /* @__PURE__ */ new Date();
5631
+ }
5522
5632
  function resolveClock(now) {
5523
5633
  if (now === void 0) return fixedClock(DEFAULT_NOW);
5634
+ if (now === "system") return systemClock();
5524
5635
  if (typeof now === "function") return () => new Date(now().getTime());
5525
5636
  return fixedClock(now);
5526
5637
  }
@@ -5585,6 +5696,28 @@ var Prng = class _Prng {
5585
5696
  return copy;
5586
5697
  }
5587
5698
  };
5699
+ var OsEntropy = class _OsEntropy extends Prng {
5700
+ constructor() {
5701
+ super(1);
5702
+ }
5703
+ nextU64() {
5704
+ const bytes = new Uint8Array(8);
5705
+ crypto.getRandomValues(bytes);
5706
+ let value = 0n;
5707
+ for (let i = 0; i < 8; i++) {
5708
+ value |= BigInt(bytes[i]) << BigInt(i * 8);
5709
+ }
5710
+ return BigInt.asUintN(64, value);
5711
+ }
5712
+ getState() {
5713
+ return 0n;
5714
+ }
5715
+ setState(_state) {
5716
+ }
5717
+ clone() {
5718
+ return new _OsEntropy();
5719
+ }
5720
+ };
5588
5721
  function deriveSeed(...parts) {
5589
5722
  let hash = 2166136261;
5590
5723
  for (const part of parts) {
@@ -6660,7 +6793,7 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6660
6793
  insert(values, rowid) {
6661
6794
  const command = this.detectCommand(values);
6662
6795
  if (command !== null) {
6663
- this.runCommand(command, values);
6796
+ this.runCommand(command, values, rowid);
6664
6797
  return 0;
6665
6798
  }
6666
6799
  const assigned = rowid ?? this.nextRowid++;
@@ -6809,18 +6942,63 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6809
6942
  }
6810
6943
  return parts.join(" ");
6811
6944
  }
6812
- /** FTS3 matchinfo default format 'pcx' simplified blob of 32-bit LE ints. */
6813
- matchinfo(cursor, _format = "pcx") {
6945
+ /** FTS3 matchinfo format encoded as native-style 32-bit little-endian integers. */
6946
+ matchinfo(cursor, format = "pcx") {
6814
6947
  const row = this.rows.get(cursor.rowid);
6815
6948
  const nPhrase = Math.max(1, cursor.phraseTerms.length);
6816
6949
  const nCol = this.columns.length;
6817
- const values = [nPhrase, nCol];
6818
- for (let p = 0; p < nPhrase; p++) {
6819
- for (let c = 0; c < nCol; c++) {
6820
- const col = this.columns[c];
6821
- const term = cursor.phraseTerms[p]?.[0] ?? "";
6822
- const tf = row ? this.termFreq(row, col, term) : 0;
6823
- values.push(tf);
6950
+ const values = [];
6951
+ for (const request of format) {
6952
+ switch (request) {
6953
+ case "p":
6954
+ values.push(nPhrase);
6955
+ break;
6956
+ case "c":
6957
+ values.push(nCol);
6958
+ break;
6959
+ case "s":
6960
+ for (let c = 0; c < nCol; c++) {
6961
+ let longest = 0;
6962
+ let run = 0;
6963
+ for (let p = 0; p < nPhrase; p++) {
6964
+ const phrase = cursor.phraseTerms[p] ?? [];
6965
+ if (row && this.phraseFreq(row, this.columns[c], phrase) > 0) {
6966
+ run++;
6967
+ longest = Math.max(longest, run);
6968
+ } else {
6969
+ run = 0;
6970
+ }
6971
+ }
6972
+ values.push(longest);
6973
+ }
6974
+ break;
6975
+ case "x":
6976
+ for (let p = 0; p < nPhrase; p++) {
6977
+ const phrase = cursor.phraseTerms[p] ?? [];
6978
+ for (let c = 0; c < nCol; c++) {
6979
+ const column = this.columns[c];
6980
+ const localHits = row ? this.phraseFreq(row, column, phrase) : 0;
6981
+ let globalHits = 0;
6982
+ let matchingRows = 0;
6983
+ for (const candidate of this.rows.values()) {
6984
+ const hits = this.phraseFreq(candidate, column, phrase);
6985
+ globalHits += hits;
6986
+ if (hits > 0) matchingRows++;
6987
+ }
6988
+ values.push(localHits, globalHits, matchingRows);
6989
+ }
6990
+ }
6991
+ break;
6992
+ case "y":
6993
+ for (let p = 0; p < nPhrase; p++) {
6994
+ const phrase = cursor.phraseTerms[p] ?? [];
6995
+ for (let c = 0; c < nCol; c++) {
6996
+ values.push(row ? this.phraseFreq(row, this.columns[c], phrase) : 0);
6997
+ }
6998
+ }
6999
+ break;
7000
+ default:
7001
+ throw new SqliteError(`unrecognized matchinfo request: ${request}`, "other");
6824
7002
  }
6825
7003
  }
6826
7004
  const buf = new Uint8Array(values.length * 4);
@@ -6868,13 +7046,9 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6868
7046
  if (!values.has(key)) return null;
6869
7047
  const v = values.get(key);
6870
7048
  if (typeof v !== "string") return null;
6871
- const otherContent = [...values.entries()].some(
6872
- ([k, val]) => k !== key && val !== null && this.columns.some((c) => c.toLowerCase() === k)
6873
- );
6874
- if (otherContent) return null;
6875
7049
  return v;
6876
7050
  }
6877
- runCommand(command, _values) {
7051
+ runCommand(command, _values, rowid) {
6878
7052
  const cmd = command.toLowerCase();
6879
7053
  if (cmd === "optimize") return;
6880
7054
  if (cmd === "rebuild") {
@@ -6889,13 +7063,15 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
6889
7063
  "other"
6890
7064
  );
6891
7065
  }
6892
- for (const rowid of [...this.rows.keys()]) this.delete(rowid);
7066
+ for (const rowid2 of [...this.rows.keys()]) this.delete(rowid2);
6893
7067
  return;
6894
7068
  }
6895
7069
  if (cmd.startsWith("merge=") || cmd.startsWith("automerge=")) {
6896
7070
  throw new SqliteError("SQL logic error", "other");
6897
7071
  }
6898
7072
  if (cmd === "delete") {
7073
+ if (rowid === void 0) throw new SqliteError("SQL logic error", "other");
7074
+ this.delete(rowid);
6899
7075
  return;
6900
7076
  }
6901
7077
  throw new SqliteError("SQL logic error", "other");
@@ -7128,11 +7304,6 @@ var Fts5VirtualTable = class _Fts5VirtualTable {
7128
7304
  }
7129
7305
  return n;
7130
7306
  }
7131
- termFreq(row, column, term) {
7132
- const needle = this.normalizeQueryTerm(term);
7133
- const tokens = row.tokensByColumn.get(column.toLowerCase()) ?? [];
7134
- return tokens.filter((t) => t.term === needle || t.term.startsWith(needle)).length;
7135
- }
7136
7307
  docLength(row) {
7137
7308
  let n = 0;
7138
7309
  for (const col of this.indexedColumns()) n += (row.tokensByColumn.get(col.toLowerCase()) ?? []).length;
@@ -7396,6 +7567,8 @@ var Table = class _Table {
7396
7567
  scanCache = null;
7397
7568
  /** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
7398
7569
  equalityHashes = null;
7570
+ /** Cached maximum rowid. `undefined` means recompute after deleting the maximum. */
7571
+ maximumRowid = null;
7399
7572
  frozen = false;
7400
7573
  constructor(name, columns, options = {}) {
7401
7574
  this.name = name;
@@ -7598,7 +7771,12 @@ var Table = class _Table {
7598
7771
  if (alias) values.set(normalizeColumnName(alias.name), targetKey);
7599
7772
  const candidate = { rowid: targetKey, values };
7600
7773
  this.validate(candidate, key);
7601
- if (targetKey !== key) this.rows.delete(key);
7774
+ if (targetKey !== key) {
7775
+ this.rows.delete(key);
7776
+ if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
7777
+ this.maximumRowid = void 0;
7778
+ }
7779
+ }
7602
7780
  this.rows.set(targetKey, candidate);
7603
7781
  this.advanceNextRowid(targetKey);
7604
7782
  this.reindexEquality(existing, candidate);
@@ -7612,6 +7790,9 @@ var Table = class _Table {
7612
7790
  if (this.withoutRowid) this.clusteredRows.delete(this.makeClusterKey(existing.values));
7613
7791
  this.unindexEquality(existing);
7614
7792
  this.invalidateScan();
7793
+ if (this.maximumRowid !== null && this.maximumRowid !== void 0 && sameRowid2(key, this.maximumRowid)) {
7794
+ this.maximumRowid = void 0;
7795
+ }
7615
7796
  return this.rows.delete(key);
7616
7797
  }
7617
7798
  *scan() {
@@ -7633,6 +7814,7 @@ var Table = class _Table {
7633
7814
  strict: this.strict
7634
7815
  });
7635
7816
  copy.nextRowid = this.nextRowid;
7817
+ copy.maximumRowid = this.maximumRowid;
7636
7818
  for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
7637
7819
  for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
7638
7820
  return copy;
@@ -7645,6 +7827,7 @@ var Table = class _Table {
7645
7827
  }
7646
7828
  /** Rebuild clustered storage after snapshot decode or bulk load. */
7647
7829
  rebuildClusteredRows() {
7830
+ this.maximumRowid = void 0;
7648
7831
  if (!this.withoutRowid) return;
7649
7832
  this.clusteredRows.clear();
7650
7833
  for (const row of this.rows.values()) {
@@ -7753,11 +7936,23 @@ var Table = class _Table {
7753
7936
  return primary[0];
7754
7937
  }
7755
7938
  allocateRowid() {
7939
+ if (!this.columns.some((column) => column.autoincrement)) {
7940
+ if (this.maximumRowid === void 0) {
7941
+ this.maximumRowid = null;
7942
+ for (const rowid of this.rows.keys()) {
7943
+ if (this.maximumRowid === null || compareRowids(rowid, this.maximumRowid) > 0) this.maximumRowid = rowid;
7944
+ }
7945
+ }
7946
+ return this.maximumRowid === null ? 1 : incrementRowid(this.maximumRowid);
7947
+ }
7756
7948
  let candidate = canonicalRowid(this.nextRowid);
7757
7949
  while (this.rows.has(candidate)) candidate = incrementRowid(candidate);
7758
7950
  return candidate;
7759
7951
  }
7760
7952
  advanceNextRowid(rowid) {
7953
+ if (this.maximumRowid === null || this.maximumRowid !== void 0 && compareRowids(rowid, this.maximumRowid) > 0) {
7954
+ this.maximumRowid = rowid;
7955
+ }
7761
7956
  if (compareRowids(rowid, this.nextRowid) >= 0) this.nextRowid = incrementRowid(rowid);
7762
7957
  }
7763
7958
  };
@@ -7832,6 +8027,8 @@ var DatabaseState = class _DatabaseState {
7832
8027
  changes = 0;
7833
8028
  totalChanges = 0;
7834
8029
  foreignKeysEnabled = false;
8030
+ /** When true, LIKE / like() are case-sensitive (SQLite `PRAGMA case_sensitive_like`). */
8031
+ caseSensitiveLike = false;
7835
8032
  schemaVersion = 0;
7836
8033
  userVersion = 0;
7837
8034
  databaseForSchema(schema, qualifiedForError) {
@@ -8276,6 +8473,7 @@ var DatabaseState = class _DatabaseState {
8276
8473
  copy.changes = this.changes;
8277
8474
  copy.totalChanges = this.totalChanges;
8278
8475
  copy.foreignKeysEnabled = this.foreignKeysEnabled;
8476
+ copy.caseSensitiveLike = this.caseSensitiveLike;
8279
8477
  copy.schemaVersion = this.schemaVersion;
8280
8478
  copy.userVersion = this.userVersion;
8281
8479
  return copy;
@@ -8295,6 +8493,7 @@ var DatabaseState = class _DatabaseState {
8295
8493
  copy.changes = this.changes;
8296
8494
  copy.totalChanges = this.totalChanges;
8297
8495
  copy.foreignKeysEnabled = this.foreignKeysEnabled;
8496
+ copy.caseSensitiveLike = this.caseSensitiveLike;
8298
8497
  copy.schemaVersion = this.schemaVersion;
8299
8498
  copy.userVersion = this.userVersion;
8300
8499
  return copy;
@@ -8356,6 +8555,7 @@ var DatabaseState = class _DatabaseState {
8356
8555
  this.changes = copy.changes;
8357
8556
  this.totalChanges = copy.totalChanges;
8358
8557
  this.foreignKeysEnabled = copy.foreignKeysEnabled;
8558
+ this.caseSensitiveLike = copy.caseSensitiveLike;
8359
8559
  this.schemaVersion = copy.schemaVersion;
8360
8560
  this.userVersion = copy.userVersion;
8361
8561
  }
@@ -8700,6 +8900,7 @@ function jsonReviver(_key, value) {
8700
8900
  export {
8701
8901
  DEFAULT_DATABASE_SEED,
8702
8902
  DEFAULT_NOW,
8903
+ OsEntropy,
8703
8904
  Prng,
8704
8905
  SqlJsonText,
8705
8906
  SqlReal,
@@ -8725,6 +8926,7 @@ export {
8725
8926
  resolveClock,
8726
8927
  sqlValueEquals,
8727
8928
  storageClassOf,
8929
+ systemClock,
8728
8930
  toInteger,
8729
8931
  tokenize,
8730
8932
  typeofSql,