@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/AGENTS.md +4 -1
- package/COMPATIBILITY-AUDIT.md +29 -15
- package/COMPATIBILITY.md +16 -8
- package/README.md +26 -15
- 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 +2 -1
- package/dist/ast/nodes.d.ts +3 -0
- package/dist/executor/env.d.ts +3 -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 +790 -211
- package/dist/index.js.map +4 -4
- package/dist/parser/index.d.ts +7 -1
- package/dist/parser/parser.d.ts +14 -4
- 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/schema/master-sql.d.ts +10 -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 +263 -72
- package/dist/unstable.js.map +3 -3
- package/dist/vtable/fts/table.d.ts +2 -3
- package/package.json +10 -3
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;
|
|
@@ -3062,14 +3097,26 @@ function inheritedCollation(expr, ctx) {
|
|
|
3062
3097
|
return null;
|
|
3063
3098
|
}
|
|
3064
3099
|
}
|
|
3065
|
-
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) {
|
|
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
|
|
3119
|
-
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));
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
4017
|
+
if (this.at("INSERT") || this.at("REPLACE")) {
|
|
3948
4018
|
return this.parseInsertStmt();
|
|
3949
4019
|
}
|
|
3950
|
-
if (this.
|
|
3951
|
-
if (this.
|
|
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
|
-
|
|
4014
|
-
|
|
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
|
|
5529
|
-
return new Parser(tokens).
|
|
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
|
-
|
|
5535
|
-
|
|
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
|
|
6836
|
-
matchinfo(cursor,
|
|
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 = [
|
|
6841
|
-
for (
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
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
|
|
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)
|
|
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,
|