@crvouga/sqlite-mem 1.13.0 → 1.14.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 +2 -0
- package/dist/executor/env.d.ts +4 -0
- package/dist/index.js +268 -55
- package/dist/index.js.map +3 -3
- package/dist/indexes/index.d.ts +2 -0
- package/dist/runtime/assert.d.ts +11 -0
- package/dist/runtime/catch.d.ts +3 -0
- package/dist/unstable.js +133 -30
- package/dist/unstable.js.map +3 -3
- package/package.json +1 -1
package/dist/indexes/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare class IndexStore {
|
|
|
5
5
|
private entries;
|
|
6
6
|
private keyValues;
|
|
7
7
|
private sortedKeys;
|
|
8
|
+
private mapsShared;
|
|
8
9
|
frozen: boolean;
|
|
9
10
|
constructor(name?: string, entries?: ReadonlyMap<string, readonly Rowid[]>, keyValues?: ReadonlyMap<string, readonly SqlValue[]>);
|
|
10
11
|
checkUnique(values: readonly SqlValue[], rowid?: Rowid): void;
|
|
@@ -39,6 +40,7 @@ export declare class IndexStore {
|
|
|
39
40
|
get size(): number;
|
|
40
41
|
private orderedKeys;
|
|
41
42
|
private assertMutable;
|
|
43
|
+
private forkMaps;
|
|
42
44
|
}
|
|
43
45
|
/** Hash-join / covering-hash key: NULL components never match. */
|
|
44
46
|
export declare function serializeIndexKey(values: readonly SqlValue[]): string | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Row } from "../storage/row.js";
|
|
2
|
+
import type { Table } from "../storage/table.js";
|
|
3
|
+
/** Always-on invariant check (Tiger Style — not stripped in production). */
|
|
4
|
+
export declare function assert(condition: boolean, message: string): asserts condition;
|
|
5
|
+
/** Exhaustive switch helper — call when a union case should be impossible. */
|
|
6
|
+
export declare function assertUnreachable(value: never, message?: string): never;
|
|
7
|
+
/** Row payload must align with table column count after insert/update. */
|
|
8
|
+
export declare function assertRowShape(table: Table, row: Row): void;
|
|
9
|
+
/** Maximum blob/string length enforced before allocation (matches bun:sqlite TOOBIG). */
|
|
10
|
+
export declare const SQLITE_MAX_LENGTH = 2147483647;
|
|
11
|
+
export declare function assertBlobLength(length: number, _feature: string): void;
|
package/dist/unstable.js
CHANGED
|
@@ -2219,6 +2219,23 @@ var mathFunctions = {
|
|
|
2219
2219
|
}
|
|
2220
2220
|
};
|
|
2221
2221
|
|
|
2222
|
+
// src/runtime/assert.ts
|
|
2223
|
+
function assert(condition, message) {
|
|
2224
|
+
if (!condition) {
|
|
2225
|
+
throw new SqliteError(`internal assertion failed: ${message}`, "other");
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
function assertRowShape(table, row) {
|
|
2229
|
+
assert(row.values.length === table.columns.length, `row.values.length !== table.columns.length for ${table.name}`);
|
|
2230
|
+
}
|
|
2231
|
+
var SQLITE_MAX_LENGTH = 2147483647;
|
|
2232
|
+
function assertBlobLength(length, _feature) {
|
|
2233
|
+
if (!Number.isFinite(length) || length < 0) return;
|
|
2234
|
+
if (length >= SQLITE_MAX_LENGTH) {
|
|
2235
|
+
throw new SqliteError(`string or blob too big`, "other", "SQLITE_TOOBIG");
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2222
2239
|
// src/expressions/like.ts
|
|
2223
2240
|
function escapeRegexChar(char) {
|
|
2224
2241
|
return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
|
|
@@ -2478,6 +2495,7 @@ var scalarFunctions = {
|
|
|
2478
2495
|
requireArgs3("randomblob", args, 1);
|
|
2479
2496
|
if (args[0] === null) return null;
|
|
2480
2497
|
const length = Math.max(0, Math.trunc(numeric(args[0])));
|
|
2498
|
+
assertBlobLength(length, "randomblob");
|
|
2481
2499
|
const out = new Uint8Array(length);
|
|
2482
2500
|
let offset = 0;
|
|
2483
2501
|
while (offset < length) {
|
|
@@ -2492,6 +2510,7 @@ var scalarFunctions = {
|
|
|
2492
2510
|
requireArgs3("zeroblob", args, 1);
|
|
2493
2511
|
if (args[0] === null) return null;
|
|
2494
2512
|
const length = Math.max(0, Math.trunc(numeric(args[0])));
|
|
2513
|
+
assertBlobLength(length, "zeroblob");
|
|
2495
2514
|
return new Uint8Array(length);
|
|
2496
2515
|
},
|
|
2497
2516
|
hex(args) {
|
|
@@ -2974,10 +2993,19 @@ function evalBinary(op, leftExpr, rightExpr, ctx) {
|
|
|
2974
2993
|
if (left === null || right === null) return null;
|
|
2975
2994
|
switch (op) {
|
|
2976
2995
|
case "+":
|
|
2996
|
+
if (storageClassOf(left) === "integer" && storageClassOf(right) === "integer") {
|
|
2997
|
+
return integerArithmetic("+", left, right);
|
|
2998
|
+
}
|
|
2977
2999
|
return asNumber(numberValue(left) + numberValue(right));
|
|
2978
3000
|
case "-":
|
|
3001
|
+
if (storageClassOf(left) === "integer" && storageClassOf(right) === "integer") {
|
|
3002
|
+
return integerArithmetic("-", left, right);
|
|
3003
|
+
}
|
|
2979
3004
|
return asNumber(numberValue(left) - numberValue(right));
|
|
2980
3005
|
case "*":
|
|
3006
|
+
if (storageClassOf(left) === "integer" && storageClassOf(right) === "integer") {
|
|
3007
|
+
return integerArithmetic("*", left, right);
|
|
3008
|
+
}
|
|
2981
3009
|
return asNumber(numberValue(left) * numberValue(right));
|
|
2982
3010
|
case "/": {
|
|
2983
3011
|
const divisor = numberValue(right);
|
|
@@ -3014,6 +3042,17 @@ function evalBinary(op, leftExpr, rightExpr, ctx) {
|
|
|
3014
3042
|
function safeIntegerResult(value) {
|
|
3015
3043
|
return value <= BigInt(Number.MAX_SAFE_INTEGER) && value >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(value) : value;
|
|
3016
3044
|
}
|
|
3045
|
+
var I64_MIN = -(2n ** 63n);
|
|
3046
|
+
var I64_MAX = 2n ** 63n - 1n;
|
|
3047
|
+
function integerArithmetic(op, left, right) {
|
|
3048
|
+
const a = integerValue(left);
|
|
3049
|
+
const b = integerValue(right);
|
|
3050
|
+
const result = op === "+" ? a + b : op === "-" ? a - b : a * b;
|
|
3051
|
+
if (result < I64_MIN || result > I64_MAX) {
|
|
3052
|
+
return asSqlReal(Number(result));
|
|
3053
|
+
}
|
|
3054
|
+
return safeIntegerResult(result);
|
|
3055
|
+
}
|
|
3017
3056
|
function compareRowValues(op, leftExprs, rightExprs, ctx) {
|
|
3018
3057
|
if (leftExprs.length !== rightExprs.length) {
|
|
3019
3058
|
throw new SqliteError("row value misused", "misuse");
|
|
@@ -5815,6 +5854,7 @@ var IndexStore = class _IndexStore {
|
|
|
5815
5854
|
entries;
|
|
5816
5855
|
keyValues;
|
|
5817
5856
|
sortedKeys;
|
|
5857
|
+
mapsShared = false;
|
|
5818
5858
|
frozen = false;
|
|
5819
5859
|
constructor(name = "index", entries, keyValues) {
|
|
5820
5860
|
this.name = name;
|
|
@@ -5868,10 +5908,22 @@ var IndexStore = class _IndexStore {
|
|
|
5868
5908
|
if (values.some((value) => value === null)) return [];
|
|
5869
5909
|
const prefix = serializeIndexEntry(values);
|
|
5870
5910
|
const exact = this.entries.get(prefix);
|
|
5871
|
-
const needle = `${prefix}|`;
|
|
5872
5911
|
const rowids = exact ? [...exact] : [];
|
|
5873
|
-
|
|
5874
|
-
|
|
5912
|
+
const keys = this.orderedKeys();
|
|
5913
|
+
let start = 0;
|
|
5914
|
+
let end = keys.length;
|
|
5915
|
+
while (start < end) {
|
|
5916
|
+
const mid = start + end >>> 1;
|
|
5917
|
+
const kv = this.keyValues.get(keys[mid]);
|
|
5918
|
+
if (!kv || prefixKeyLess(kv, values)) start = mid + 1;
|
|
5919
|
+
else end = mid;
|
|
5920
|
+
}
|
|
5921
|
+
for (let i = start; i < keys.length; i++) {
|
|
5922
|
+
const kv = this.keyValues.get(keys[i]);
|
|
5923
|
+
if (!kv || !prefixKeyMatches(kv, values)) break;
|
|
5924
|
+
const key = keys[i];
|
|
5925
|
+
if (key === prefix) continue;
|
|
5926
|
+
rowids.push(...this.entries.get(key) ?? []);
|
|
5875
5927
|
}
|
|
5876
5928
|
return rowids;
|
|
5877
5929
|
}
|
|
@@ -5880,9 +5932,12 @@ var IndexStore = class _IndexStore {
|
|
|
5880
5932
|
* `op` applies to the first key component.
|
|
5881
5933
|
*/
|
|
5882
5934
|
rangeLookup(op, bound, bound2) {
|
|
5883
|
-
const rowids = [];
|
|
5884
5935
|
const keys = this.orderedKeys();
|
|
5885
|
-
|
|
5936
|
+
const rowids = [];
|
|
5937
|
+
const start = lowerBoundKeyValues(keys, this.keyValues, bound, op === ">" ? "gt" : op === ">=" ? "ge" : "any");
|
|
5938
|
+
const end = op === "<" || op === "<=" ? upperBoundKeyValues(keys, this.keyValues, bound, op === "<" ? "lt" : "le") : op === "between" && bound2 !== void 0 ? upperBoundKeyValues(keys, this.keyValues, bound2, "le") : keys.length;
|
|
5939
|
+
for (let i = start; i < end; i++) {
|
|
5940
|
+
const key = keys[i];
|
|
5886
5941
|
const values = this.keyValues.get(key);
|
|
5887
5942
|
if (!values || values[0] === void 0 || values[0] === null) continue;
|
|
5888
5943
|
const cmp = compareSerializedOrder(values[0], bound);
|
|
@@ -5939,7 +5994,12 @@ var IndexStore = class _IndexStore {
|
|
|
5939
5994
|
this.sortedKeys = null;
|
|
5940
5995
|
}
|
|
5941
5996
|
clone() {
|
|
5942
|
-
|
|
5997
|
+
const copy = new _IndexStore(this.name);
|
|
5998
|
+
copy.entries = this.entries;
|
|
5999
|
+
copy.keyValues = this.keyValues;
|
|
6000
|
+
copy.sortedKeys = this.sortedKeys;
|
|
6001
|
+
copy.mapsShared = true;
|
|
6002
|
+
return copy;
|
|
5943
6003
|
}
|
|
5944
6004
|
freeze() {
|
|
5945
6005
|
this.frozen = true;
|
|
@@ -5995,6 +6055,18 @@ var IndexStore = class _IndexStore {
|
|
|
5995
6055
|
}
|
|
5996
6056
|
assertMutable() {
|
|
5997
6057
|
if (this.frozen) throw new SqliteError("internal: cannot mutate a frozen index", "other");
|
|
6058
|
+
this.forkMaps();
|
|
6059
|
+
}
|
|
6060
|
+
forkMaps() {
|
|
6061
|
+
if (!this.mapsShared) return;
|
|
6062
|
+
const entries = /* @__PURE__ */ new Map();
|
|
6063
|
+
for (const [key, rowids] of this.entries) entries.set(key, [...rowids]);
|
|
6064
|
+
const keyValues = /* @__PURE__ */ new Map();
|
|
6065
|
+
for (const [key, values] of this.keyValues) keyValues.set(key, [...values]);
|
|
6066
|
+
this.entries = entries;
|
|
6067
|
+
this.keyValues = keyValues;
|
|
6068
|
+
this.mapsShared = false;
|
|
6069
|
+
this.sortedKeys = null;
|
|
5998
6070
|
}
|
|
5999
6071
|
};
|
|
6000
6072
|
function serializeIndexKey(values) {
|
|
@@ -6033,6 +6105,53 @@ function serializeValue(value) {
|
|
|
6033
6105
|
function compareSerializedOrder(left, right) {
|
|
6034
6106
|
return compareSql(left, right) ?? 0;
|
|
6035
6107
|
}
|
|
6108
|
+
function prefixKeyLess(keyValues, prefix) {
|
|
6109
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
6110
|
+
const cmp = compareSerializedOrder(keyValues[i] ?? null, prefix[i]);
|
|
6111
|
+
if (cmp !== 0) return cmp < 0;
|
|
6112
|
+
}
|
|
6113
|
+
return false;
|
|
6114
|
+
}
|
|
6115
|
+
function prefixKeyMatches(keyValues, prefix) {
|
|
6116
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
6117
|
+
if (compareSerializedOrder(keyValues[i] ?? null, prefix[i]) !== 0) return false;
|
|
6118
|
+
}
|
|
6119
|
+
return true;
|
|
6120
|
+
}
|
|
6121
|
+
function lowerBoundKeyValues(keys, keyValues, bound, mode) {
|
|
6122
|
+
let lo = 0;
|
|
6123
|
+
let hi = keys.length;
|
|
6124
|
+
while (lo < hi) {
|
|
6125
|
+
const mid = lo + hi >>> 1;
|
|
6126
|
+
const value = keyValues.get(keys[mid])?.[0];
|
|
6127
|
+
if (value === void 0 || value === null) {
|
|
6128
|
+
lo = mid + 1;
|
|
6129
|
+
continue;
|
|
6130
|
+
}
|
|
6131
|
+
const cmp = compareSerializedOrder(value, bound);
|
|
6132
|
+
const before = mode === "gt" ? cmp <= 0 : mode === "ge" ? cmp < 0 : false;
|
|
6133
|
+
if (before) lo = mid + 1;
|
|
6134
|
+
else hi = mid;
|
|
6135
|
+
}
|
|
6136
|
+
return lo;
|
|
6137
|
+
}
|
|
6138
|
+
function upperBoundKeyValues(keys, keyValues, bound, mode) {
|
|
6139
|
+
let lo = 0;
|
|
6140
|
+
let hi = keys.length;
|
|
6141
|
+
while (lo < hi) {
|
|
6142
|
+
const mid = lo + hi >>> 1;
|
|
6143
|
+
const value = keyValues.get(keys[mid])?.[0];
|
|
6144
|
+
if (value === void 0 || value === null) {
|
|
6145
|
+
lo = mid + 1;
|
|
6146
|
+
continue;
|
|
6147
|
+
}
|
|
6148
|
+
const cmp = compareSerializedOrder(value, bound);
|
|
6149
|
+
const after = mode === "lt" ? cmp >= 0 : cmp > 0;
|
|
6150
|
+
if (after) hi = mid;
|
|
6151
|
+
else lo = mid + 1;
|
|
6152
|
+
}
|
|
6153
|
+
return lo;
|
|
6154
|
+
}
|
|
6036
6155
|
function sameRowid(left, right) {
|
|
6037
6156
|
return typeof left === "bigint" || typeof right === "bigint" ? BigInt(left) === BigInt(right) : left === right;
|
|
6038
6157
|
}
|
|
@@ -6553,6 +6672,9 @@ function parseFts5Query(input) {
|
|
|
6553
6672
|
}
|
|
6554
6673
|
if (p.kind === "TERM") {
|
|
6555
6674
|
next();
|
|
6675
|
+
if (p.prefix && (p.value === "" || p.value.includes("*"))) {
|
|
6676
|
+
throw new SqliteError(`unknown special query: ${p.value}`, "other");
|
|
6677
|
+
}
|
|
6556
6678
|
return { type: "term", value: p.value, prefix: p.prefix, column: null, columns: null };
|
|
6557
6679
|
}
|
|
6558
6680
|
throw new SqliteError(`fts5: syntax error near "${displayTok(p)}"`, "syntax");
|
|
@@ -6719,17 +6841,7 @@ function displayTok(t) {
|
|
|
6719
6841
|
}
|
|
6720
6842
|
}
|
|
6721
6843
|
function parseFts3Query(input) {
|
|
6722
|
-
|
|
6723
|
-
return parseFts5Query(input);
|
|
6724
|
-
} catch {
|
|
6725
|
-
const terms = input.split(/\s+/).filter(Boolean).filter((t) => !/^(AND|OR|NOT|NEAR)$/i.test(t));
|
|
6726
|
-
if (terms.length === 0) return { type: "true" };
|
|
6727
|
-
const nodes = terms.map((t) => {
|
|
6728
|
-
const prefix = t.endsWith("*");
|
|
6729
|
-
return { type: "term", value: prefix ? t.slice(0, -1) : t, prefix, column: null, columns: null };
|
|
6730
|
-
});
|
|
6731
|
-
return nodes.length === 1 ? nodes[0] : { type: "and", children: nodes };
|
|
6732
|
-
}
|
|
6844
|
+
return parseFts5Query(input);
|
|
6733
6845
|
}
|
|
6734
6846
|
|
|
6735
6847
|
// src/vtable/fts/porter.ts
|
|
@@ -7792,11 +7904,6 @@ function rowValues(values) {
|
|
|
7792
7904
|
}
|
|
7793
7905
|
return result;
|
|
7794
7906
|
}
|
|
7795
|
-
function cloneRow(row) {
|
|
7796
|
-
const values = new Array(row.values.length);
|
|
7797
|
-
for (let i = 0; i < row.values.length; i++) values[i] = cloneSqlValue(row.values[i]);
|
|
7798
|
-
return { rowid: row.rowid, values };
|
|
7799
|
-
}
|
|
7800
7907
|
function isValueArray(values) {
|
|
7801
7908
|
return Array.isArray(values);
|
|
7802
7909
|
}
|
|
@@ -7935,6 +8042,7 @@ var Table = class _Table {
|
|
|
7935
8042
|
this.scanCache = null;
|
|
7936
8043
|
}
|
|
7937
8044
|
commitRow(row) {
|
|
8045
|
+
assertRowShape(this, row);
|
|
7938
8046
|
this.indexEquality(row);
|
|
7939
8047
|
this.invalidateScan();
|
|
7940
8048
|
}
|
|
@@ -8157,16 +8265,11 @@ var Table = class _Table {
|
|
|
8157
8265
|
copy.nextRowid = this.nextRowid;
|
|
8158
8266
|
copy.maximumRowid = this.maximumRowid;
|
|
8159
8267
|
if (this.slab) {
|
|
8160
|
-
|
|
8161
|
-
for (const row of this.slab.scan()) {
|
|
8162
|
-
copy.rows.set(row.rowid, cloneRow(row));
|
|
8163
|
-
if (max === null || compareRowids(row.rowid, max) > 0) max = row.rowid;
|
|
8164
|
-
}
|
|
8165
|
-
copy.maximumRowid = max ?? void 0;
|
|
8268
|
+
for (const row of this.slab.scan()) copy.rows.set(row.rowid, row);
|
|
8166
8269
|
} else {
|
|
8167
|
-
for (const [rowid, row] of this.rows) copy.rows.set(rowid,
|
|
8270
|
+
for (const [rowid, row] of this.rows) copy.rows.set(rowid, row);
|
|
8168
8271
|
}
|
|
8169
|
-
for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey,
|
|
8272
|
+
for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, row);
|
|
8170
8273
|
return copy;
|
|
8171
8274
|
}
|
|
8172
8275
|
freeze() {
|