@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/AGENTS.md +4 -1
- package/COMPATIBILITY-AUDIT.md +29 -15
- package/COMPATIBILITY.md +16 -8
- package/README.md +17 -12
- package/compat/coverage.json +779 -729
- package/compat/divergences.json +124 -0
- package/compat/requirements.json +43 -43
- package/compat/scenario-types.ts +63 -0
- package/compat/scenarios.ts +851 -0
- package/compat/smoke-baseline.json +15 -0
- package/dist/api/database.d.ts +12 -3
- package/dist/api/statement.d.ts +1 -1
- package/dist/ast/nodes.d.ts +12 -1
- package/dist/executor/env.d.ts +1 -1
- package/dist/executor/select.d.ts +2 -1
- package/dist/executor/triggers.d.ts +2 -2
- package/dist/expressions/context.d.ts +3 -1
- package/dist/expressions/like.d.ts +3 -2
- package/dist/functions/registry.d.ts +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +698 -187
- package/dist/index.js.map +4 -4
- package/dist/lexer/tokenize.d.ts +1 -1
- package/dist/parser/parser.d.ts +2 -2
- package/dist/runtime/clock.d.ts +4 -2
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/options.d.ts +11 -3
- package/dist/runtime/prng.d.ts +11 -0
- package/dist/storage/database-state.d.ts +2 -0
- package/dist/storage/table.d.ts +2 -0
- package/dist/types/value.d.ts +5 -0
- package/dist/unstable.d.ts +3 -3
- package/dist/unstable.js +266 -64
- package/dist/unstable.js.map +3 -3
- package/dist/vtable/fts/table.d.ts +2 -3
- package/package.json +5 -2
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
|
|
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
|
-
|
|
291
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
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(
|
|
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
|
|
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
|
|
3108
|
-
const
|
|
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(
|
|
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.
|
|
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.
|
|
4007
|
+
if (this.at("INSERT") || this.at("REPLACE")) {
|
|
3933
4008
|
return this.parseInsertStmt();
|
|
3934
4009
|
}
|
|
3935
|
-
if (this.
|
|
3936
|
-
if (this.
|
|
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
|
-
|
|
3999
|
-
|
|
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
|
|
6813
|
-
matchinfo(cursor,
|
|
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 = [
|
|
6818
|
-
for (
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
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
|
|
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)
|
|
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,
|