@crvouga/sqlite-mem 0.1.0 → 1.0.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 +154 -0
- package/COMPATIBILITY-AUDIT.md +7 -9
- package/COMPATIBILITY.md +5 -4
- package/README.md +166 -16
- package/compat/coverage.json +797 -512
- package/compat/requirements.json +1 -1
- package/dist/api/database.d.ts +97 -12
- package/dist/api/statement.d.ts +65 -18
- package/dist/ast/nodes.d.ts +11 -2
- package/dist/constraints/check.d.ts +5 -6
- package/dist/errors/index.d.ts +36 -3
- package/dist/executor/attach.d.ts +3 -4
- package/dist/executor/ddl.d.ts +3 -4
- package/dist/executor/dml.d.ts +4 -4
- package/dist/executor/env.d.ts +9 -11
- package/dist/executor/execute.d.ts +3 -4
- package/dist/executor/pragma.d.ts +3 -4
- package/dist/executor/result.d.ts +10 -6
- package/dist/executor/select.d.ts +4 -5
- package/dist/executor/simple-select.d.ts +3 -4
- package/dist/executor/triggers.d.ts +6 -7
- package/dist/executor/vtable.d.ts +5 -6
- package/dist/expressions/context.d.ts +5 -5
- package/dist/expressions/equals.d.ts +3 -0
- package/dist/expressions/eval.d.ts +9 -4
- package/dist/expressions/index.d.ts +3 -4
- package/dist/expressions/like.d.ts +13 -3
- package/dist/functions/aggregate.d.ts +1 -2
- package/dist/functions/datetime.d.ts +1 -2
- package/dist/functions/extensions.d.ts +1 -2
- package/dist/functions/index.d.ts +8 -9
- package/dist/functions/json.d.ts +3 -4
- package/dist/functions/math.d.ts +1 -2
- package/dist/functions/registry.d.ts +5 -6
- package/dist/functions/scalar.d.ts +2 -3
- package/dist/functions/table-valued.d.ts +3 -4
- package/dist/functions/window.d.ts +1 -2
- package/dist/index.d.ts +27 -12
- package/dist/index.js +986 -203
- package/dist/index.js.map +4 -4
- package/dist/indexes/index.d.ts +17 -4
- package/dist/json/index.d.ts +7 -8
- package/dist/json/jsonb.d.ts +1 -2
- package/dist/json/ops.d.ts +8 -3
- package/dist/json/parse.d.ts +2 -3
- package/dist/json/path.d.ts +1 -2
- package/dist/json/stringify.d.ts +1 -2
- package/dist/json/tvf.d.ts +1 -2
- package/dist/json/types.d.ts +0 -1
- package/dist/lexer/tokenize.d.ts +7 -1
- package/dist/parser/index.d.ts +11 -5
- package/dist/parser/parser.d.ts +3 -3
- package/dist/planner/access.d.ts +13 -6
- package/dist/planner/index.d.ts +1 -2
- package/dist/runtime/clock.d.ts +8 -2
- package/dist/runtime/index.d.ts +3 -4
- package/dist/runtime/options.d.ts +4 -6
- package/dist/runtime/prng.d.ts +12 -1
- package/dist/schema/catalog.d.ts +2 -3
- package/dist/serialization/codec.d.ts +17 -2
- package/dist/serialization/index.d.ts +1 -2
- package/dist/storage/database-state.d.ts +16 -8
- package/dist/storage/index.d.ts +6 -7
- package/dist/storage/row.d.ts +1 -2
- package/dist/storage/table.d.ts +8 -4
- package/dist/transactions/manager.d.ts +2 -3
- package/dist/types/collation.d.ts +1 -2
- package/dist/types/strict.d.ts +6 -0
- package/dist/types/value.d.ts +30 -2
- package/dist/unstable.d.ts +20 -0
- package/dist/unstable.js +8734 -0
- package/dist/unstable.js.map +7 -0
- package/dist/vtable/fts/options.d.ts +0 -1
- package/dist/vtable/fts/porter.d.ts +0 -1
- package/dist/vtable/fts/query.d.ts +0 -1
- package/dist/vtable/fts/table.d.ts +5 -6
- package/dist/vtable/fts/tokenize.d.ts +1 -2
- package/dist/vtable/fts5.d.ts +1 -2
- package/dist/vtable/index.d.ts +2 -3
- package/dist/vtable/modules.d.ts +2 -3
- package/package.json +19 -10
- package/compat/gate-report.json +0 -424
- package/dist/api/database.d.ts.map +0 -1
- package/dist/api/statement.d.ts.map +0 -1
- package/dist/ast/nodes.d.ts.map +0 -1
- package/dist/constraints/check.d.ts.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/executor/attach.d.ts.map +0 -1
- package/dist/executor/ddl.d.ts.map +0 -1
- package/dist/executor/dml.d.ts.map +0 -1
- package/dist/executor/env.d.ts.map +0 -1
- package/dist/executor/execute.d.ts.map +0 -1
- package/dist/executor/pragma.d.ts.map +0 -1
- package/dist/executor/result.d.ts.map +0 -1
- package/dist/executor/select.d.ts.map +0 -1
- package/dist/executor/simple-select.d.ts.map +0 -1
- package/dist/executor/triggers.d.ts.map +0 -1
- package/dist/executor/vtable.d.ts.map +0 -1
- package/dist/expressions/context.d.ts.map +0 -1
- package/dist/expressions/eval.d.ts.map +0 -1
- package/dist/expressions/index.d.ts.map +0 -1
- package/dist/expressions/like.d.ts.map +0 -1
- package/dist/functions/aggregate.d.ts.map +0 -1
- package/dist/functions/datetime.d.ts.map +0 -1
- package/dist/functions/extensions.d.ts.map +0 -1
- package/dist/functions/index.d.ts.map +0 -1
- package/dist/functions/json.d.ts.map +0 -1
- package/dist/functions/math.d.ts.map +0 -1
- package/dist/functions/registry.d.ts.map +0 -1
- package/dist/functions/scalar.d.ts.map +0 -1
- package/dist/functions/table-valued.d.ts.map +0 -1
- package/dist/functions/window.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/indexes/index.d.ts.map +0 -1
- package/dist/json/index.d.ts.map +0 -1
- package/dist/json/jsonb.d.ts.map +0 -1
- package/dist/json/ops.d.ts.map +0 -1
- package/dist/json/parse.d.ts.map +0 -1
- package/dist/json/path.d.ts.map +0 -1
- package/dist/json/stringify.d.ts.map +0 -1
- package/dist/json/tvf.d.ts.map +0 -1
- package/dist/json/types.d.ts.map +0 -1
- package/dist/lexer/tokenize.d.ts.map +0 -1
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/parser.d.ts.map +0 -1
- package/dist/planner/access.d.ts.map +0 -1
- package/dist/planner/index.d.ts.map +0 -1
- package/dist/runtime/clock.d.ts.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/options.d.ts.map +0 -1
- package/dist/runtime/prng.d.ts.map +0 -1
- package/dist/schema/catalog.d.ts.map +0 -1
- package/dist/serialization/codec.d.ts.map +0 -1
- package/dist/serialization/index.d.ts.map +0 -1
- package/dist/storage/database-state.d.ts.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/row.d.ts.map +0 -1
- package/dist/storage/table.d.ts.map +0 -1
- package/dist/transactions/manager.d.ts.map +0 -1
- package/dist/types/collation.d.ts.map +0 -1
- package/dist/types/value.d.ts.map +0 -1
- package/dist/vtable/fts/options.d.ts.map +0 -1
- package/dist/vtable/fts/porter.d.ts.map +0 -1
- package/dist/vtable/fts/query.d.ts.map +0 -1
- package/dist/vtable/fts/table.d.ts.map +0 -1
- package/dist/vtable/fts/tokenize.d.ts.map +0 -1
- package/dist/vtable/fts5.d.ts.map +0 -1
- package/dist/vtable/index.d.ts.map +0 -1
- package/dist/vtable/modules.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
// src/errors/index.ts
|
|
2
2
|
var SqliteError = class extends Error {
|
|
3
|
+
/** Coarse error class (constraint vs syntax vs missing object, …). */
|
|
3
4
|
category;
|
|
5
|
+
/** SQLite result-code name such as `SQLITE_CONSTRAINT_UNIQUE`. */
|
|
4
6
|
sqliteCode;
|
|
5
|
-
|
|
7
|
+
/** Same value as {@link sqliteCode} (Node `err.code` convention). */
|
|
8
|
+
code;
|
|
9
|
+
/**
|
|
10
|
+
* @param message - Human-readable error text.
|
|
11
|
+
* @param category - Coarse class; defaults to `"other"`.
|
|
12
|
+
* @param sqliteCode - SQLite result-code name; defaults to `"SQLITE_ERROR"`.
|
|
13
|
+
*/
|
|
14
|
+
constructor(message, category = "other", sqliteCode = "SQLITE_ERROR") {
|
|
6
15
|
super(message);
|
|
7
16
|
this.name = "SqliteError";
|
|
8
17
|
this.category = category;
|
|
9
18
|
this.sqliteCode = sqliteCode;
|
|
19
|
+
this.code = sqliteCode;
|
|
10
20
|
}
|
|
11
21
|
};
|
|
12
22
|
var TriggerRaiseError = class extends SqliteError {
|
|
@@ -1176,10 +1186,14 @@ var Parser = class {
|
|
|
1176
1186
|
if (!this.match("ON")) return null;
|
|
1177
1187
|
this.expect("CONFLICT");
|
|
1178
1188
|
let targetColumns = null;
|
|
1189
|
+
let targetExprs = null;
|
|
1179
1190
|
if (this.match("LPAREN")) {
|
|
1180
1191
|
targetColumns = [];
|
|
1192
|
+
targetExprs = [];
|
|
1181
1193
|
do {
|
|
1182
|
-
|
|
1194
|
+
const expr = this.parseExprPrec(PREC.ADD);
|
|
1195
|
+
targetExprs.push(expr);
|
|
1196
|
+
targetColumns.push(expr.type === "column" ? expr.name : "");
|
|
1183
1197
|
} while (this.match("COMMA"));
|
|
1184
1198
|
this.expect("RPAREN");
|
|
1185
1199
|
}
|
|
@@ -1187,14 +1201,14 @@ var Parser = class {
|
|
|
1187
1201
|
if (this.match("WHERE")) targetWhere = this.parseExpr();
|
|
1188
1202
|
this.expect("DO");
|
|
1189
1203
|
if (this.match("NOTHING")) {
|
|
1190
|
-
return { targetColumns, targetWhere, action: "nothing" };
|
|
1204
|
+
return { targetColumns, targetExprs, targetWhere, action: "nothing" };
|
|
1191
1205
|
}
|
|
1192
1206
|
this.expect("UPDATE");
|
|
1193
1207
|
this.expect("SET");
|
|
1194
1208
|
const set = this.parseSetItems();
|
|
1195
1209
|
let where = null;
|
|
1196
1210
|
if (this.match("WHERE")) where = this.parseExpr();
|
|
1197
|
-
return { targetColumns, targetWhere, action: { set, where } };
|
|
1211
|
+
return { targetColumns, targetExprs, targetWhere, action: { set, where } };
|
|
1198
1212
|
}
|
|
1199
1213
|
parseSetItems() {
|
|
1200
1214
|
const items = [];
|
|
@@ -1361,7 +1375,8 @@ var Parser = class {
|
|
|
1361
1375
|
columns: [],
|
|
1362
1376
|
constraints: [],
|
|
1363
1377
|
asSelect,
|
|
1364
|
-
withoutRowid: false
|
|
1378
|
+
withoutRowid: false,
|
|
1379
|
+
strict: false
|
|
1365
1380
|
};
|
|
1366
1381
|
}
|
|
1367
1382
|
this.expect("LPAREN");
|
|
@@ -1376,12 +1391,30 @@ var Parser = class {
|
|
|
1376
1391
|
} while (this.match("COMMA"));
|
|
1377
1392
|
this.expect("RPAREN");
|
|
1378
1393
|
let withoutRowid = false;
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
if (
|
|
1382
|
-
|
|
1394
|
+
let strict = false;
|
|
1395
|
+
while (!this.at("SEMI") && !this.at("EOF")) {
|
|
1396
|
+
if (this.match("WITHOUT")) {
|
|
1397
|
+
const word = this.parseIdent();
|
|
1398
|
+
if (word.toUpperCase() !== "ROWID") this.syntaxError("expected ROWID after WITHOUT");
|
|
1399
|
+
withoutRowid = true;
|
|
1400
|
+
} else {
|
|
1401
|
+
const word = this.parseIdent();
|
|
1402
|
+
if (word.toUpperCase() !== "STRICT") this.syntaxError(`unexpected table option ${word}`);
|
|
1403
|
+
strict = true;
|
|
1404
|
+
}
|
|
1405
|
+
this.match("COMMA");
|
|
1383
1406
|
}
|
|
1384
|
-
return {
|
|
1407
|
+
return {
|
|
1408
|
+
type: "create_table",
|
|
1409
|
+
ifNotExists,
|
|
1410
|
+
temp,
|
|
1411
|
+
name,
|
|
1412
|
+
columns,
|
|
1413
|
+
constraints,
|
|
1414
|
+
asSelect: null,
|
|
1415
|
+
withoutRowid,
|
|
1416
|
+
strict
|
|
1417
|
+
};
|
|
1385
1418
|
}
|
|
1386
1419
|
parseColumnDef() {
|
|
1387
1420
|
const name = this.parseIdent();
|
|
@@ -1507,7 +1540,8 @@ var Parser = class {
|
|
|
1507
1540
|
this.expect("RPAREN");
|
|
1508
1541
|
}
|
|
1509
1542
|
const { onDelete, onUpdate } = this.parseFkActions();
|
|
1510
|
-
|
|
1543
|
+
const { deferrable, initiallyDeferred } = this.parseDeferrable();
|
|
1544
|
+
return { type: "references", table, columns, onDelete, onUpdate, deferrable, initiallyDeferred };
|
|
1511
1545
|
}
|
|
1512
1546
|
parseTableConstraint() {
|
|
1513
1547
|
let name = null;
|
|
@@ -1554,20 +1588,40 @@ var Parser = class {
|
|
|
1554
1588
|
this.expect("RPAREN");
|
|
1555
1589
|
}
|
|
1556
1590
|
const { onDelete, onUpdate } = this.parseFkActions();
|
|
1557
|
-
|
|
1591
|
+
const { deferrable, initiallyDeferred } = this.parseDeferrable();
|
|
1592
|
+
return {
|
|
1593
|
+
type: "foreign_key",
|
|
1594
|
+
columns,
|
|
1595
|
+
refTable,
|
|
1596
|
+
refColumns,
|
|
1597
|
+
onDelete,
|
|
1598
|
+
onUpdate,
|
|
1599
|
+
name,
|
|
1600
|
+
deferrable,
|
|
1601
|
+
initiallyDeferred
|
|
1602
|
+
};
|
|
1558
1603
|
}
|
|
1559
1604
|
this.syntaxError("expected table constraint");
|
|
1560
1605
|
}
|
|
1561
1606
|
parseIndexedColumns() {
|
|
1562
1607
|
const cols = [];
|
|
1563
1608
|
do {
|
|
1564
|
-
const
|
|
1609
|
+
const expr = this.parseExprPrec(PREC.ADD);
|
|
1565
1610
|
let collate = null;
|
|
1611
|
+
let inner = expr;
|
|
1612
|
+
if (inner.type === "collate") {
|
|
1613
|
+
collate = inner.collation;
|
|
1614
|
+
inner = inner.expr;
|
|
1615
|
+
}
|
|
1566
1616
|
if (this.match("COLLATE")) collate = this.parseIdent();
|
|
1567
1617
|
let order = null;
|
|
1568
1618
|
if (this.match("ASC")) order = "ASC";
|
|
1569
1619
|
else if (this.match("DESC")) order = "DESC";
|
|
1570
|
-
|
|
1620
|
+
if (inner.type === "column" && inner.table === null) {
|
|
1621
|
+
cols.push({ name: inner.name, collate, order });
|
|
1622
|
+
} else {
|
|
1623
|
+
cols.push({ name: "", collate, order, expr: inner });
|
|
1624
|
+
}
|
|
1571
1625
|
} while (this.match("COMMA"));
|
|
1572
1626
|
return cols;
|
|
1573
1627
|
}
|
|
@@ -1610,6 +1664,23 @@ var Parser = class {
|
|
|
1610
1664
|
}
|
|
1611
1665
|
return { onDelete, onUpdate };
|
|
1612
1666
|
}
|
|
1667
|
+
parseDeferrable() {
|
|
1668
|
+
let deferrable = false;
|
|
1669
|
+
let initiallyDeferred = false;
|
|
1670
|
+
if (this.match("NOT")) {
|
|
1671
|
+
this.expect("DEFERRABLE");
|
|
1672
|
+
} else if (this.match("DEFERRABLE")) {
|
|
1673
|
+
deferrable = true;
|
|
1674
|
+
}
|
|
1675
|
+
if (this.match("INITIALLY")) {
|
|
1676
|
+
if (this.match("DEFERRED")) initiallyDeferred = true;
|
|
1677
|
+
else {
|
|
1678
|
+
this.expect("IMMEDIATE");
|
|
1679
|
+
initiallyDeferred = false;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
return { deferrable, initiallyDeferred: deferrable && initiallyDeferred };
|
|
1683
|
+
}
|
|
1613
1684
|
parseOrConflict() {
|
|
1614
1685
|
if (!this.match("OR")) return null;
|
|
1615
1686
|
return this.parseConflictAction();
|
|
@@ -2214,13 +2285,19 @@ var Parser = class {
|
|
|
2214
2285
|
start = this.parseFrameBound(true);
|
|
2215
2286
|
end = { kind: "current_row" };
|
|
2216
2287
|
}
|
|
2217
|
-
|
|
2218
|
-
if (this.
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2288
|
+
let exclude = null;
|
|
2289
|
+
if (this.match("EXCLUDE")) {
|
|
2290
|
+
if (this.match("NO")) {
|
|
2291
|
+
this.expect("OTHERS");
|
|
2292
|
+
exclude = "no_others";
|
|
2293
|
+
} else if (this.match("CURRENT")) {
|
|
2294
|
+
this.expect("ROW");
|
|
2295
|
+
exclude = "current_row";
|
|
2296
|
+
} else if (this.match("GROUP")) exclude = "group";
|
|
2297
|
+
else if (this.match("TIES")) exclude = "ties";
|
|
2298
|
+
else this.syntaxError("expected EXCLUDE CURRENT ROW, GROUP, TIES, or NO OTHERS");
|
|
2299
|
+
}
|
|
2300
|
+
return { type, start, end, exclude };
|
|
2224
2301
|
}
|
|
2225
2302
|
parseFrameBound(precedingSide) {
|
|
2226
2303
|
if (this.match("UNBOUNDED")) {
|
|
@@ -2268,6 +2345,9 @@ var DEFAULT_DATABASE_SEED = 1;
|
|
|
2268
2345
|
// src/runtime/prng.ts
|
|
2269
2346
|
var Prng = class _Prng {
|
|
2270
2347
|
state;
|
|
2348
|
+
/**
|
|
2349
|
+
* @param seed - Engine seed. `0` is replaced with a non-zero constant.
|
|
2350
|
+
*/
|
|
2271
2351
|
constructor(seed = 1) {
|
|
2272
2352
|
let s = typeof seed === "bigint" ? seed : BigInt(seed | 0);
|
|
2273
2353
|
if (s === 0n) s = 0x9e3779b97f4a7c15n;
|
|
@@ -2289,10 +2369,16 @@ var Prng = class _Prng {
|
|
|
2289
2369
|
if (signed === -(1n << 63n)) signed += 1n;
|
|
2290
2370
|
return signed;
|
|
2291
2371
|
}
|
|
2372
|
+
/** Next float in `[0, 1)` from 53 bits of {@link nextU64}. */
|
|
2292
2373
|
nextFloat() {
|
|
2293
2374
|
const bits = this.nextU64();
|
|
2294
2375
|
return Number(bits >> 11n) / Number(1n << 53n);
|
|
2295
2376
|
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Next integer in `[min, max]` (inclusive).
|
|
2379
|
+
*
|
|
2380
|
+
* @throws {RangeError} If `max < min`.
|
|
2381
|
+
*/
|
|
2296
2382
|
nextInt(min, max) {
|
|
2297
2383
|
if (max < min) throw new RangeError("max < min");
|
|
2298
2384
|
const span = max - min + 1;
|
|
@@ -2302,29 +2388,21 @@ var Prng = class _Prng {
|
|
|
2302
2388
|
getState() {
|
|
2303
2389
|
return this.state;
|
|
2304
2390
|
}
|
|
2391
|
+
/** Restore unsigned 64-bit engine state from {@link getState}. */
|
|
2305
2392
|
setState(state) {
|
|
2306
2393
|
this.state = BigInt.asUintN(64, state);
|
|
2307
2394
|
}
|
|
2395
|
+
/** Independent copy with the same engine state. */
|
|
2308
2396
|
clone() {
|
|
2309
2397
|
const copy = new _Prng(1);
|
|
2310
2398
|
copy.state = this.state;
|
|
2311
2399
|
return copy;
|
|
2312
2400
|
}
|
|
2313
2401
|
};
|
|
2314
|
-
function deriveSeed(...parts) {
|
|
2315
|
-
let hash = 2166136261;
|
|
2316
|
-
for (const part of parts) {
|
|
2317
|
-
const text2 = String(part);
|
|
2318
|
-
for (let i = 0; i < text2.length; i++) {
|
|
2319
|
-
hash ^= text2.charCodeAt(i);
|
|
2320
|
-
hash = Math.imul(hash, 16777619);
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
return hash | 0;
|
|
2324
|
-
}
|
|
2325
2402
|
|
|
2326
2403
|
// src/types/value.ts
|
|
2327
2404
|
var SqlReal = class {
|
|
2405
|
+
/** IEEE float payload (`-0` already canonicalized to `+0`). */
|
|
2328
2406
|
value;
|
|
2329
2407
|
constructor(value) {
|
|
2330
2408
|
this.value = canonicalizeNumber(value);
|
|
@@ -3458,6 +3536,23 @@ function jsonArrowPath(right) {
|
|
|
3458
3536
|
function extractOne(root, path) {
|
|
3459
3537
|
return pathGet(root, parseJsonPath(path));
|
|
3460
3538
|
}
|
|
3539
|
+
function arrayInsertJson(root, path, value) {
|
|
3540
|
+
const steps = parseJsonPath(path);
|
|
3541
|
+
const last = steps[steps.length - 1];
|
|
3542
|
+
if (!last || last.kind === "root" || last.kind === "key") {
|
|
3543
|
+
throw new SqliteError("JSON path error", "other");
|
|
3544
|
+
}
|
|
3545
|
+
const result = cloneJson(root);
|
|
3546
|
+
const parentInfo = pathParent(result, steps);
|
|
3547
|
+
if (parentInfo?.parent.kind !== "array") return result;
|
|
3548
|
+
const parent = parentInfo.parent;
|
|
3549
|
+
let idx = parentInfo.index;
|
|
3550
|
+
if (last.kind === "fromEnd") idx = parent.elements.length - last.n;
|
|
3551
|
+
if (last.kind === "append") idx = parent.elements.length;
|
|
3552
|
+
if (idx < 0 || idx > parent.elements.length) return result;
|
|
3553
|
+
parent.elements.splice(idx, 0, cloneJson(value));
|
|
3554
|
+
return result;
|
|
3555
|
+
}
|
|
3461
3556
|
function mutateJson(root, path, value, mode) {
|
|
3462
3557
|
const steps = parseJsonPath(path);
|
|
3463
3558
|
if (steps.length <= 1) {
|
|
@@ -3678,6 +3773,26 @@ function textOf(value) {
|
|
|
3678
3773
|
if (isSqlJsonText(value)) return value.value;
|
|
3679
3774
|
return String(value);
|
|
3680
3775
|
}
|
|
3776
|
+
function arrayInsertArgs(args, asJsonb) {
|
|
3777
|
+
if (args.length === 0) {
|
|
3778
|
+
throw new SqliteError("wrong number of arguments to function json_array_insert()", "misuse");
|
|
3779
|
+
}
|
|
3780
|
+
if (args.length % 2 === 0) {
|
|
3781
|
+
throw new SqliteError("json_array_insert() requires an odd number of arguments", "misuse");
|
|
3782
|
+
}
|
|
3783
|
+
if (args.some((arg) => arg === null)) return null;
|
|
3784
|
+
try {
|
|
3785
|
+
let node = ensureJson(args[0]);
|
|
3786
|
+
for (let i = 1; i < args.length; i += 2) {
|
|
3787
|
+
const path = textOf(args[i]);
|
|
3788
|
+
const value = sqlValueToJsonNode(args[i + 1]);
|
|
3789
|
+
node = arrayInsertJson(node, path, value);
|
|
3790
|
+
}
|
|
3791
|
+
return asJsonb ? jsonNodeToSql(node, "jsonb") : jsonNodeToSql(node, "json");
|
|
3792
|
+
} catch (e) {
|
|
3793
|
+
wrapJsonError(e);
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3681
3796
|
function mutateArgs(args, mode, asJsonb) {
|
|
3682
3797
|
if (args.length === 0) throw new SqliteError(`wrong number of arguments to function json_${mode}()`, "misuse");
|
|
3683
3798
|
if (args.length % 2 === 0) {
|
|
@@ -3816,6 +3931,12 @@ var jsonScalarFunctions = {
|
|
|
3816
3931
|
jsonb_insert(args) {
|
|
3817
3932
|
return mutateArgs(args, "insert", true);
|
|
3818
3933
|
},
|
|
3934
|
+
json_array_insert(args) {
|
|
3935
|
+
return arrayInsertArgs(args, false);
|
|
3936
|
+
},
|
|
3937
|
+
jsonb_array_insert(args) {
|
|
3938
|
+
return arrayInsertArgs(args, true);
|
|
3939
|
+
},
|
|
3819
3940
|
json_replace(args) {
|
|
3820
3941
|
return mutateArgs(args, "replace", false);
|
|
3821
3942
|
},
|
|
@@ -4859,6 +4980,10 @@ var scalarFunctions = {
|
|
|
4859
4980
|
requireArgs3("sqlite_log", args, 2);
|
|
4860
4981
|
return null;
|
|
4861
4982
|
},
|
|
4983
|
+
load_extension(args) {
|
|
4984
|
+
requireArgs3("load_extension", args, 1, 2);
|
|
4985
|
+
throw new SqliteError("not authorized", "misuse");
|
|
4986
|
+
},
|
|
4862
4987
|
like(args) {
|
|
4863
4988
|
requireArgs3("like", args, 2, 3);
|
|
4864
4989
|
if (args[0] === null || args[1] === null || args[2] === null) return null;
|
|
@@ -5599,12 +5724,20 @@ function raiseActionName(expr) {
|
|
|
5599
5724
|
var IndexStore = class _IndexStore {
|
|
5600
5725
|
name;
|
|
5601
5726
|
entries;
|
|
5602
|
-
|
|
5727
|
+
keyValues;
|
|
5728
|
+
sortedKeys;
|
|
5729
|
+
frozen = false;
|
|
5730
|
+
constructor(name = "index", entries, keyValues) {
|
|
5603
5731
|
this.name = name;
|
|
5604
5732
|
this.entries = /* @__PURE__ */ new Map();
|
|
5733
|
+
this.keyValues = /* @__PURE__ */ new Map();
|
|
5734
|
+
this.sortedKeys = null;
|
|
5605
5735
|
if (entries) {
|
|
5606
5736
|
for (const [key, rowids] of entries) this.entries.set(key, [...rowids]);
|
|
5607
5737
|
}
|
|
5738
|
+
if (keyValues) {
|
|
5739
|
+
for (const [key, values] of keyValues) this.keyValues.set(key, [...values]);
|
|
5740
|
+
}
|
|
5608
5741
|
}
|
|
5609
5742
|
checkUnique(values, rowid) {
|
|
5610
5743
|
const key = serializeIndexKey(values);
|
|
@@ -5622,12 +5755,15 @@ var IndexStore = class _IndexStore {
|
|
|
5622
5755
|
}
|
|
5623
5756
|
}
|
|
5624
5757
|
insert(values, rowid, unique = true) {
|
|
5758
|
+
this.assertMutable();
|
|
5625
5759
|
const key = serializeIndexKey(values);
|
|
5626
5760
|
if (key === null) return;
|
|
5627
5761
|
if (unique) this.checkUnique(values, rowid);
|
|
5628
5762
|
const existing = this.entries.get(key);
|
|
5629
5763
|
if (!existing) {
|
|
5630
5764
|
this.entries.set(key, [rowid]);
|
|
5765
|
+
this.keyValues.set(key, [...values]);
|
|
5766
|
+
this.sortedKeys = null;
|
|
5631
5767
|
return;
|
|
5632
5768
|
}
|
|
5633
5769
|
for (const id of existing) if (sameRowid(id, rowid)) return;
|
|
@@ -5638,30 +5774,112 @@ var IndexStore = class _IndexStore {
|
|
|
5638
5774
|
if (key === null) return [];
|
|
5639
5775
|
return this.entries.get(key) ?? [];
|
|
5640
5776
|
}
|
|
5777
|
+
/** Leftmost prefix match: INDEX(a,b) used for WHERE a = ?. */
|
|
5778
|
+
lookupPrefix(values) {
|
|
5779
|
+
if (values.length === 0) return [];
|
|
5780
|
+
const prefix = serializeIndexKey(values);
|
|
5781
|
+
if (prefix === null) return [];
|
|
5782
|
+
const exact = this.entries.get(prefix);
|
|
5783
|
+
const needle = `${prefix}|`;
|
|
5784
|
+
const rowids = exact ? [...exact] : [];
|
|
5785
|
+
for (const [key, ids] of this.entries) {
|
|
5786
|
+
if (key.startsWith(needle)) rowids.push(...ids);
|
|
5787
|
+
}
|
|
5788
|
+
return rowids;
|
|
5789
|
+
}
|
|
5790
|
+
/**
|
|
5791
|
+
* Ordered scan of rowids for a single-column comparison.
|
|
5792
|
+
* `op` applies to the first key component.
|
|
5793
|
+
*/
|
|
5794
|
+
rangeLookup(op, bound, bound2) {
|
|
5795
|
+
const rowids = [];
|
|
5796
|
+
const keys = this.orderedKeys();
|
|
5797
|
+
for (const key of keys) {
|
|
5798
|
+
const values = this.keyValues.get(key);
|
|
5799
|
+
if (!values || values[0] === void 0) continue;
|
|
5800
|
+
const cmp = compareSerializedOrder(values[0], bound);
|
|
5801
|
+
let ok = false;
|
|
5802
|
+
if (op === ">") ok = cmp > 0;
|
|
5803
|
+
else if (op === ">=") ok = cmp >= 0;
|
|
5804
|
+
else if (op === "<") ok = cmp < 0;
|
|
5805
|
+
else if (op === "<=") ok = cmp <= 0;
|
|
5806
|
+
else if (op === "between" && bound2 !== void 0) {
|
|
5807
|
+
ok = cmp >= 0 && compareSerializedOrder(values[0], bound2) <= 0;
|
|
5808
|
+
}
|
|
5809
|
+
if (ok) {
|
|
5810
|
+
const ids = this.entries.get(key);
|
|
5811
|
+
if (ids) rowids.push(...ids);
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
return rowids;
|
|
5815
|
+
}
|
|
5816
|
+
orderedRowids(desc = false) {
|
|
5817
|
+
const keys = this.orderedKeys();
|
|
5818
|
+
const rowids = [];
|
|
5819
|
+
const seq = desc ? [...keys].reverse() : keys;
|
|
5820
|
+
for (const key of seq) {
|
|
5821
|
+
const ids = this.entries.get(key);
|
|
5822
|
+
if (ids) rowids.push(...ids);
|
|
5823
|
+
}
|
|
5824
|
+
return rowids;
|
|
5825
|
+
}
|
|
5641
5826
|
remove(values, rowid) {
|
|
5827
|
+
this.assertMutable();
|
|
5642
5828
|
const key = serializeIndexKey(values);
|
|
5643
5829
|
if (key === null) return false;
|
|
5644
5830
|
const existing = this.entries.get(key);
|
|
5645
5831
|
if (!existing) return false;
|
|
5646
5832
|
if (rowid === void 0) {
|
|
5647
5833
|
this.entries.delete(key);
|
|
5834
|
+
this.keyValues.delete(key);
|
|
5835
|
+
this.sortedKeys = null;
|
|
5648
5836
|
return true;
|
|
5649
5837
|
}
|
|
5650
5838
|
const index = existing.findIndex((id) => sameRowid(id, rowid));
|
|
5651
5839
|
if (index < 0) return false;
|
|
5652
5840
|
existing.splice(index, 1);
|
|
5653
|
-
if (existing.length === 0)
|
|
5841
|
+
if (existing.length === 0) {
|
|
5842
|
+
this.entries.delete(key);
|
|
5843
|
+
this.keyValues.delete(key);
|
|
5844
|
+
this.sortedKeys = null;
|
|
5845
|
+
}
|
|
5654
5846
|
return true;
|
|
5655
5847
|
}
|
|
5656
5848
|
clear() {
|
|
5849
|
+
this.assertMutable();
|
|
5657
5850
|
this.entries.clear();
|
|
5851
|
+
this.keyValues.clear();
|
|
5852
|
+
this.sortedKeys = null;
|
|
5658
5853
|
}
|
|
5659
5854
|
clone() {
|
|
5660
|
-
return new _IndexStore(this.name, this.entries);
|
|
5855
|
+
return new _IndexStore(this.name, this.entries, this.keyValues);
|
|
5856
|
+
}
|
|
5857
|
+
freeze() {
|
|
5858
|
+
this.frozen = true;
|
|
5661
5859
|
}
|
|
5662
5860
|
get size() {
|
|
5663
5861
|
return this.entries.size;
|
|
5664
5862
|
}
|
|
5863
|
+
orderedKeys() {
|
|
5864
|
+
if (this.sortedKeys) return this.sortedKeys;
|
|
5865
|
+
const keys = [...this.entries.keys()];
|
|
5866
|
+
keys.sort((a, b) => {
|
|
5867
|
+
const left = this.keyValues.get(a);
|
|
5868
|
+
const right = this.keyValues.get(b);
|
|
5869
|
+
if (!left || !right) return a < b ? -1 : a > b ? 1 : 0;
|
|
5870
|
+
const n = Math.min(left.length, right.length);
|
|
5871
|
+
for (let i = 0; i < n; i++) {
|
|
5872
|
+
const cmp = compareSql(left[i] ?? null, right[i] ?? null);
|
|
5873
|
+
if (cmp !== 0) return cmp ?? 0;
|
|
5874
|
+
}
|
|
5875
|
+
return left.length - right.length;
|
|
5876
|
+
});
|
|
5877
|
+
this.sortedKeys = keys;
|
|
5878
|
+
return keys;
|
|
5879
|
+
}
|
|
5880
|
+
assertMutable() {
|
|
5881
|
+
if (this.frozen) throw new SqliteError("internal: cannot mutate a frozen index", "other");
|
|
5882
|
+
}
|
|
5665
5883
|
};
|
|
5666
5884
|
function serializeIndexKey(values) {
|
|
5667
5885
|
if (values.some((value) => value === null)) return null;
|
|
@@ -5689,10 +5907,70 @@ function serializeValue(value) {
|
|
|
5689
5907
|
for (const byte of value) hex += byte.toString(16).padStart(2, "0");
|
|
5690
5908
|
return `b:${hex}`;
|
|
5691
5909
|
}
|
|
5910
|
+
function compareSerializedOrder(left, right) {
|
|
5911
|
+
return compareSql(left, right) ?? 0;
|
|
5912
|
+
}
|
|
5692
5913
|
function sameRowid(left, right) {
|
|
5693
5914
|
return typeof left === "bigint" || typeof right === "bigint" ? BigInt(left) === BigInt(right) : left === right;
|
|
5694
5915
|
}
|
|
5695
5916
|
|
|
5917
|
+
// src/types/strict.ts
|
|
5918
|
+
var STRICT_TYPES = /* @__PURE__ */ new Set(["INT", "INTEGER", "REAL", "TEXT", "BLOB", "ANY"]);
|
|
5919
|
+
function isStrictTypeName(typeName) {
|
|
5920
|
+
if (!typeName) return false;
|
|
5921
|
+
return STRICT_TYPES.has(typeName.trim().toUpperCase());
|
|
5922
|
+
}
|
|
5923
|
+
function applyStrictValue(value, typeName, table, column) {
|
|
5924
|
+
if (value === null) return null;
|
|
5925
|
+
const declared = typeName.trim().toUpperCase();
|
|
5926
|
+
const storage = storageClassOf(value);
|
|
5927
|
+
if (declared === "ANY") return value;
|
|
5928
|
+
if (declared === "TEXT") {
|
|
5929
|
+
if (storage === "text") return typeof value === "string" ? value : String(value.value);
|
|
5930
|
+
if (storage === "integer" || storage === "real") return String(isSqlReal(value) ? value.value : value);
|
|
5931
|
+
throw cannotStore(storage, declared, table, column);
|
|
5932
|
+
}
|
|
5933
|
+
if (declared === "INT" || declared === "INTEGER") {
|
|
5934
|
+
if (storage === "integer") return value;
|
|
5935
|
+
if (storage === "real") {
|
|
5936
|
+
const n = isSqlReal(value) ? value.value : Number(value);
|
|
5937
|
+
if (Number.isInteger(n)) return n;
|
|
5938
|
+
throw cannotStore(storage, declared, table, column);
|
|
5939
|
+
}
|
|
5940
|
+
if (storage === "text") {
|
|
5941
|
+
const n = coerceToNumber(value);
|
|
5942
|
+
if (n !== null && Number.isInteger(n) && String(value).trim() === String(n)) return n;
|
|
5943
|
+
throw cannotStore(storage, declared, table, column);
|
|
5944
|
+
}
|
|
5945
|
+
throw cannotStore(storage, declared, table, column);
|
|
5946
|
+
}
|
|
5947
|
+
if (declared === "REAL") {
|
|
5948
|
+
if (storage === "real") return isSqlReal(value) ? value : asSqlReal(Number(value));
|
|
5949
|
+
if (storage === "integer") return asSqlReal(typeof value === "bigint" ? Number(value) : Number(value));
|
|
5950
|
+
if (storage === "text") {
|
|
5951
|
+
const n = coerceToNumber(value);
|
|
5952
|
+
if (n !== null) return asSqlReal(n);
|
|
5953
|
+
}
|
|
5954
|
+
throw cannotStore(storage, declared, table, column);
|
|
5955
|
+
}
|
|
5956
|
+
if (declared === "BLOB") {
|
|
5957
|
+
if (storage === "blob") return value;
|
|
5958
|
+
throw cannotStore(storage, declared, table, column);
|
|
5959
|
+
}
|
|
5960
|
+
throw new SqliteError(`unknown datatype for ${table}.${column}: "${typeName}"`, "other");
|
|
5961
|
+
}
|
|
5962
|
+
function cannotStore(storage, declared, table, column) {
|
|
5963
|
+
const shown = storage === "integer" ? "INT" : storage.toUpperCase();
|
|
5964
|
+
throw new SqliteError(
|
|
5965
|
+
`cannot store ${shown} value in ${declared} column ${table}.${column}`,
|
|
5966
|
+
"datatype_mismatch",
|
|
5967
|
+
"SQLITE_CONSTRAINT_DATATYPE"
|
|
5968
|
+
);
|
|
5969
|
+
}
|
|
5970
|
+
function unknownStrictType(table, column, typeName) {
|
|
5971
|
+
throw new SqliteError(`unknown datatype for ${table}.${column}: "${typeName}"`, "other");
|
|
5972
|
+
}
|
|
5973
|
+
|
|
5696
5974
|
// src/vtable/fts/options.ts
|
|
5697
5975
|
var OPTION_NAMES = /* @__PURE__ */ new Set([
|
|
5698
5976
|
"tokenize",
|
|
@@ -7235,11 +7513,13 @@ var Table = class _Table {
|
|
|
7235
7513
|
indexes;
|
|
7236
7514
|
originalSql;
|
|
7237
7515
|
withoutRowid;
|
|
7516
|
+
strict;
|
|
7238
7517
|
/** Clustered PK key string → row for WITHOUT ROWID tables. */
|
|
7239
7518
|
clusteredRows;
|
|
7240
7519
|
scanCache = null;
|
|
7241
7520
|
/** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
|
|
7242
7521
|
equalityHashes = null;
|
|
7522
|
+
frozen = false;
|
|
7243
7523
|
constructor(name, columns, options = {}) {
|
|
7244
7524
|
this.name = name;
|
|
7245
7525
|
this.columns = columns.map(cloneColumn);
|
|
@@ -7249,6 +7529,7 @@ var Table = class _Table {
|
|
|
7249
7529
|
this.indexes = [...options.indexes ?? []];
|
|
7250
7530
|
this.originalSql = options.originalSql ?? null;
|
|
7251
7531
|
this.withoutRowid = options.withoutRowid ?? false;
|
|
7532
|
+
this.strict = options.strict ?? false;
|
|
7252
7533
|
this.clusteredRows = /* @__PURE__ */ new Map();
|
|
7253
7534
|
this.scanCache = null;
|
|
7254
7535
|
}
|
|
@@ -7284,7 +7565,7 @@ var Table = class _Table {
|
|
|
7284
7565
|
}
|
|
7285
7566
|
/** True when the table has no user constraints that DML must enforce beyond hidden rowid. */
|
|
7286
7567
|
isUnconstrained() {
|
|
7287
|
-
if (this.withoutRowid || this.indexes.length > 0 || this.constraints.length > 0) return false;
|
|
7568
|
+
if (this.withoutRowid || this.strict || this.indexes.length > 0 || this.constraints.length > 0) return false;
|
|
7288
7569
|
for (const column of this.columns) {
|
|
7289
7570
|
if (column.notNull || column.primaryKey || column.unique || column.generated || column.defaultExpr) return false;
|
|
7290
7571
|
}
|
|
@@ -7311,7 +7592,7 @@ var Table = class _Table {
|
|
|
7311
7592
|
return rows;
|
|
7312
7593
|
}
|
|
7313
7594
|
ensureEqualityHash(columnLower) {
|
|
7314
|
-
if (this.rows.size < EQUALITY_HASH_MIN_ROWS) return false;
|
|
7595
|
+
if (this.frozen || this.rows.size < EQUALITY_HASH_MIN_ROWS) return false;
|
|
7315
7596
|
const column = this.columns.find((item) => item.nameLower === columnLower);
|
|
7316
7597
|
if (!column) return false;
|
|
7317
7598
|
this.equalityHashes ??= /* @__PURE__ */ new Map();
|
|
@@ -7361,6 +7642,7 @@ var Table = class _Table {
|
|
|
7361
7642
|
}
|
|
7362
7643
|
}
|
|
7363
7644
|
insert(input, options) {
|
|
7645
|
+
this.assertMutable();
|
|
7364
7646
|
const supplied = isInsertRow(input) ? input : { values: input };
|
|
7365
7647
|
const values = options?.prepared ? supplied.values instanceof Map ? supplied.values : rowValues(supplied.values) : this.prepareValues(supplied.values);
|
|
7366
7648
|
if (this.withoutRowid) {
|
|
@@ -7403,6 +7685,7 @@ var Table = class _Table {
|
|
|
7403
7685
|
return rowid;
|
|
7404
7686
|
}
|
|
7405
7687
|
update(rowid, updates) {
|
|
7688
|
+
this.assertMutable();
|
|
7406
7689
|
const key = canonicalRowid(rowid);
|
|
7407
7690
|
const existing = this.rows.get(key);
|
|
7408
7691
|
if (!existing) return void 0;
|
|
@@ -7445,6 +7728,7 @@ var Table = class _Table {
|
|
|
7445
7728
|
return candidate;
|
|
7446
7729
|
}
|
|
7447
7730
|
delete(rowid) {
|
|
7731
|
+
this.assertMutable();
|
|
7448
7732
|
const key = canonicalRowid(rowid);
|
|
7449
7733
|
const existing = this.rows.get(key);
|
|
7450
7734
|
if (!existing) return false;
|
|
@@ -7468,13 +7752,20 @@ var Table = class _Table {
|
|
|
7468
7752
|
constraints: this.constraints,
|
|
7469
7753
|
indexes: this.indexes,
|
|
7470
7754
|
originalSql: this.originalSql,
|
|
7471
|
-
withoutRowid: this.withoutRowid
|
|
7755
|
+
withoutRowid: this.withoutRowid,
|
|
7756
|
+
strict: this.strict
|
|
7472
7757
|
});
|
|
7473
7758
|
copy.nextRowid = this.nextRowid;
|
|
7474
7759
|
for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
|
|
7475
7760
|
for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
|
|
7476
7761
|
return copy;
|
|
7477
7762
|
}
|
|
7763
|
+
freeze() {
|
|
7764
|
+
this.frozen = true;
|
|
7765
|
+
}
|
|
7766
|
+
assertMutable() {
|
|
7767
|
+
if (this.frozen) throw new SqliteError("internal: cannot mutate a frozen table", "other");
|
|
7768
|
+
}
|
|
7478
7769
|
/** Rebuild clustered storage after snapshot decode or bulk load. */
|
|
7479
7770
|
rebuildClusteredRows() {
|
|
7480
7771
|
if (!this.withoutRowid) return;
|
|
@@ -7491,7 +7782,10 @@ var Table = class _Table {
|
|
|
7491
7782
|
const result = /* @__PURE__ */ new Map();
|
|
7492
7783
|
for (const column of this.columns) {
|
|
7493
7784
|
const key = normalizeColumnName(column.name);
|
|
7494
|
-
result.set(
|
|
7785
|
+
result.set(
|
|
7786
|
+
key,
|
|
7787
|
+
this.strict ? applyStrictValue(cloneSqlValue(supplied.get(key) ?? null), column.typeName ?? "", this.name, column.name) : applyAffinity(cloneSqlValue(supplied.get(key) ?? null), column.affinity)
|
|
7788
|
+
);
|
|
7495
7789
|
}
|
|
7496
7790
|
return result;
|
|
7497
7791
|
}
|
|
@@ -7833,6 +8127,12 @@ var DatabaseState = class _DatabaseState {
|
|
|
7833
8127
|
if (stmt.columns.length === 0) {
|
|
7834
8128
|
throw new SqliteError("table must have at least one column", "syntax");
|
|
7835
8129
|
}
|
|
8130
|
+
if (stmt.strict) {
|
|
8131
|
+
for (const definition of stmt.columns) {
|
|
8132
|
+
const typeName = definition.typeName?.trim() ?? "";
|
|
8133
|
+
if (!isStrictTypeName(typeName)) unknownStrictType(bare, definition.name, typeName || "null");
|
|
8134
|
+
}
|
|
8135
|
+
}
|
|
7836
8136
|
const normalizedConstraints = [...stmt.constraints];
|
|
7837
8137
|
for (const definition of stmt.columns) {
|
|
7838
8138
|
for (const constraint of definition.constraints) {
|
|
@@ -7846,7 +8146,9 @@ var DatabaseState = class _DatabaseState {
|
|
|
7846
8146
|
refColumns: constraint.columns,
|
|
7847
8147
|
onDelete: constraint.onDelete,
|
|
7848
8148
|
onUpdate: constraint.onUpdate,
|
|
7849
|
-
name: null
|
|
8149
|
+
name: null,
|
|
8150
|
+
deferrable: constraint.deferrable,
|
|
8151
|
+
initiallyDeferred: constraint.initiallyDeferred
|
|
7850
8152
|
});
|
|
7851
8153
|
}
|
|
7852
8154
|
}
|
|
@@ -7854,7 +8156,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
7854
8156
|
const table = new Table(bare, columns, {
|
|
7855
8157
|
constraints: normalizedConstraints,
|
|
7856
8158
|
originalSql,
|
|
7857
|
-
withoutRowid: stmt.withoutRowid
|
|
8159
|
+
withoutRowid: stmt.withoutRowid,
|
|
8160
|
+
strict: stmt.strict
|
|
7858
8161
|
});
|
|
7859
8162
|
if (stmt.withoutRowid) {
|
|
7860
8163
|
const pk = columns.filter((column) => column.primaryKey);
|
|
@@ -7937,7 +8240,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
7937
8240
|
return this.databaseForSchema(oldSchema, oldName).renameTable(oldBare, splitQualifiedName(newName).bare);
|
|
7938
8241
|
}
|
|
7939
8242
|
const oldKey = keyOf(oldBare);
|
|
7940
|
-
const table = this.
|
|
8243
|
+
const table = this.getWritableTable(oldName);
|
|
7941
8244
|
const { schema: newSchema, bare: newBare } = splitQualifiedName(newName);
|
|
7942
8245
|
if (newSchema !== null) {
|
|
7943
8246
|
throw new SqliteError(`no such table: ${newName}`, "no_such_table");
|
|
@@ -7946,11 +8249,13 @@ var DatabaseState = class _DatabaseState {
|
|
|
7946
8249
|
this.tables.delete(oldKey);
|
|
7947
8250
|
table.name = newBare;
|
|
7948
8251
|
this.tables.set(keyOf(newBare), table);
|
|
7949
|
-
for (const index of this.indexes.values()) {
|
|
7950
|
-
if (keyOf(index.tableName)
|
|
8252
|
+
for (const index of [...this.indexes.values()]) {
|
|
8253
|
+
if (keyOf(index.tableName) !== oldKey) continue;
|
|
8254
|
+
this.ensureWritableIndex(index).tableName = newBare;
|
|
7951
8255
|
}
|
|
7952
|
-
for (const trigger of this.triggers
|
|
7953
|
-
if (keyOf(trigger.tableName)
|
|
8256
|
+
for (const [key, trigger] of this.triggers) {
|
|
8257
|
+
if (keyOf(trigger.tableName) !== oldKey) continue;
|
|
8258
|
+
this.triggers.set(key, { ...trigger, tableName: newBare });
|
|
7954
8259
|
}
|
|
7955
8260
|
this.schemaVersion++;
|
|
7956
8261
|
return table;
|
|
@@ -7985,7 +8290,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
7985
8290
|
return deleted;
|
|
7986
8291
|
}
|
|
7987
8292
|
createIndex(stmt, originalSql = null) {
|
|
7988
|
-
const table = this.
|
|
8293
|
+
const table = this.getWritableTable(stmt.table);
|
|
7989
8294
|
const { schema, bare } = splitQualifiedName(stmt.name);
|
|
7990
8295
|
const target = schema !== null ? this.databaseForSchema(schema, stmt.name) : this.databaseForTable(table);
|
|
7991
8296
|
const key = keyOf(bare);
|
|
@@ -7993,6 +8298,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
7993
8298
|
if (existing && stmt.ifNotExists) return existing;
|
|
7994
8299
|
target.assertSchemaNameAvailable(bare);
|
|
7995
8300
|
for (const indexed of stmt.columns) {
|
|
8301
|
+
if (indexed.expr) continue;
|
|
7996
8302
|
if (!table.columns.some((column) => keyOf(column.name) === keyOf(indexed.name))) {
|
|
7997
8303
|
throw new SqliteError(`no such column: ${indexed.name}`, "no_such_column");
|
|
7998
8304
|
}
|
|
@@ -8022,7 +8328,10 @@ var DatabaseState = class _DatabaseState {
|
|
|
8022
8328
|
}
|
|
8023
8329
|
target.indexes.delete(key);
|
|
8024
8330
|
const table = target.tables.get(keyOf(index.tableName));
|
|
8025
|
-
if (table)
|
|
8331
|
+
if (table) {
|
|
8332
|
+
const writable = target.ensureWritableTable(table);
|
|
8333
|
+
writable.indexes = writable.indexes.filter((item) => keyOf(item) !== key);
|
|
8334
|
+
}
|
|
8026
8335
|
target.schemaVersion++;
|
|
8027
8336
|
return true;
|
|
8028
8337
|
}
|
|
@@ -8106,6 +8415,71 @@ var DatabaseState = class _DatabaseState {
|
|
|
8106
8415
|
copy.totalChanges = this.totalChanges;
|
|
8107
8416
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8108
8417
|
copy.schemaVersion = this.schemaVersion;
|
|
8418
|
+
copy.userVersion = this.userVersion;
|
|
8419
|
+
return copy;
|
|
8420
|
+
}
|
|
8421
|
+
/** Share table/index objects with the live state. Callers must freeze first. */
|
|
8422
|
+
cloneShallow() {
|
|
8423
|
+
const copy = new _DatabaseState();
|
|
8424
|
+
copy.tables = new Map(this.tables);
|
|
8425
|
+
for (const [key, table] of this.virtualTables) copy.virtualTables.set(key, table.clone());
|
|
8426
|
+
copy.views = new Map(this.views);
|
|
8427
|
+
copy.indexes = new Map(this.indexes);
|
|
8428
|
+
copy.triggers = new Map(this.triggers);
|
|
8429
|
+
for (const [name, attached] of this.attached) {
|
|
8430
|
+
copy.attached.set(name, { state: attached.state.cloneShallow(), filename: attached.filename });
|
|
8431
|
+
}
|
|
8432
|
+
copy.lastInsertRowid = this.lastInsertRowid;
|
|
8433
|
+
copy.changes = this.changes;
|
|
8434
|
+
copy.totalChanges = this.totalChanges;
|
|
8435
|
+
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8436
|
+
copy.schemaVersion = this.schemaVersion;
|
|
8437
|
+
copy.userVersion = this.userVersion;
|
|
8438
|
+
return copy;
|
|
8439
|
+
}
|
|
8440
|
+
freezeShared() {
|
|
8441
|
+
for (const table of this.tables.values()) table.freeze();
|
|
8442
|
+
for (const index of this.indexes.values()) index.store.freeze();
|
|
8443
|
+
for (const attached of this.attached.values()) attached.state.freezeShared();
|
|
8444
|
+
}
|
|
8445
|
+
thawShared() {
|
|
8446
|
+
for (const table of this.tables.values()) table.frozen = false;
|
|
8447
|
+
for (const index of this.indexes.values()) index.store.frozen = false;
|
|
8448
|
+
for (const attached of this.attached.values()) attached.state.thawShared();
|
|
8449
|
+
}
|
|
8450
|
+
getWritableTable(name) {
|
|
8451
|
+
const { schema, bare } = splitQualifiedName(name);
|
|
8452
|
+
if (schema !== null) {
|
|
8453
|
+
return this.databaseForSchema(schema, name).getWritableTable(bare);
|
|
8454
|
+
}
|
|
8455
|
+
const table = this.getTable(bare);
|
|
8456
|
+
return this.ensureWritableTable(table);
|
|
8457
|
+
}
|
|
8458
|
+
ensureWritableTable(table) {
|
|
8459
|
+
if (!table.frozen) return table;
|
|
8460
|
+
const key = keyOf(table.name);
|
|
8461
|
+
const copy = table.clone();
|
|
8462
|
+
this.tables.set(key, copy);
|
|
8463
|
+
return copy;
|
|
8464
|
+
}
|
|
8465
|
+
getWritableIndex(name) {
|
|
8466
|
+
const key = keyOf(name);
|
|
8467
|
+
const index = this.indexes.get(key);
|
|
8468
|
+
if (!index) throw new SqliteError(`no such index: ${name}`, "other");
|
|
8469
|
+
return this.ensureWritableIndex(index);
|
|
8470
|
+
}
|
|
8471
|
+
ensureWritableIndex(index) {
|
|
8472
|
+
if (!index.store.frozen) return index;
|
|
8473
|
+
const copy = {
|
|
8474
|
+
name: index.name,
|
|
8475
|
+
tableName: index.tableName,
|
|
8476
|
+
unique: index.unique,
|
|
8477
|
+
columns: structuredClone(index.columns),
|
|
8478
|
+
where: index.where ? structuredClone(index.where) : null,
|
|
8479
|
+
originalSql: index.originalSql,
|
|
8480
|
+
store: index.store.clone()
|
|
8481
|
+
};
|
|
8482
|
+
this.indexes.set(keyOf(index.name), copy);
|
|
8109
8483
|
return copy;
|
|
8110
8484
|
}
|
|
8111
8485
|
replaceWith(state, options) {
|
|
@@ -8121,6 +8495,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8121
8495
|
this.totalChanges = copy.totalChanges;
|
|
8122
8496
|
this.foreignKeysEnabled = copy.foreignKeysEnabled;
|
|
8123
8497
|
this.schemaVersion = copy.schemaVersion;
|
|
8498
|
+
this.userVersion = copy.userVersion;
|
|
8124
8499
|
}
|
|
8125
8500
|
assertSchemaNameAvailable(name) {
|
|
8126
8501
|
const key = keyOf(name);
|
|
@@ -8312,7 +8687,8 @@ function encodeDatabaseState(state, runtime) {
|
|
|
8312
8687
|
constraints: table.constraints,
|
|
8313
8688
|
indexes: [...table.indexes].sort(compareNames),
|
|
8314
8689
|
originalSql: table.originalSql,
|
|
8315
|
-
withoutRowid: table.withoutRowid || void 0
|
|
8690
|
+
withoutRowid: table.withoutRowid || void 0,
|
|
8691
|
+
strict: table.strict || void 0
|
|
8316
8692
|
});
|
|
8317
8693
|
writer.value(table.nextRowid);
|
|
8318
8694
|
const rows = [...table.rows.values()].sort((a, b) => compareRowids2(a.rowid, b.rowid));
|
|
@@ -8349,7 +8725,7 @@ function decodeDatabaseState(snapshot) {
|
|
|
8349
8725
|
throw new SqliteError("invalid sqlite-mem snapshot magic", "other");
|
|
8350
8726
|
const version = reader.u32();
|
|
8351
8727
|
if (version !== VERSION && version !== VERSION_V1) {
|
|
8352
|
-
throw new SqliteError(`unsupported sqlite-mem snapshot version: ${version}`, "
|
|
8728
|
+
throw new SqliteError(`unsupported sqlite-mem snapshot version: ${version}`, "snapshot_version", "SQLITE_FORMAT");
|
|
8353
8729
|
}
|
|
8354
8730
|
const state = new DatabaseState();
|
|
8355
8731
|
state.foreignKeysEnabled = reader.u8() !== 0;
|
|
@@ -8364,7 +8740,8 @@ function decodeDatabaseState(snapshot) {
|
|
|
8364
8740
|
constraints: meta.constraints,
|
|
8365
8741
|
indexes: meta.indexes,
|
|
8366
8742
|
originalSql: meta.originalSql,
|
|
8367
|
-
withoutRowid: meta.withoutRowid ?? false
|
|
8743
|
+
withoutRowid: meta.withoutRowid ?? false,
|
|
8744
|
+
strict: meta.strict ?? false
|
|
8368
8745
|
});
|
|
8369
8746
|
table.nextRowid = asRowid2(reader.value());
|
|
8370
8747
|
const rowCount = reader.u32();
|
|
@@ -8478,7 +8855,8 @@ var TransactionManager = class {
|
|
|
8478
8855
|
if (this.inTransaction) {
|
|
8479
8856
|
throw new SqliteError("cannot start a transaction within a transaction", "transaction", "SQLITE_ERROR");
|
|
8480
8857
|
}
|
|
8481
|
-
this.
|
|
8858
|
+
this.state.freezeShared();
|
|
8859
|
+
this.transactionSnapshot = this.state.cloneShallow();
|
|
8482
8860
|
this.transactionPrngState = this.prng.getState();
|
|
8483
8861
|
this.savepoints = [];
|
|
8484
8862
|
this.startedBySavepoint = false;
|
|
@@ -8489,6 +8867,7 @@ var TransactionManager = class {
|
|
|
8489
8867
|
this.transactionPrngState = null;
|
|
8490
8868
|
this.savepoints = [];
|
|
8491
8869
|
this.startedBySavepoint = false;
|
|
8870
|
+
this.state.thawShared();
|
|
8492
8871
|
}
|
|
8493
8872
|
rollback(savepoint) {
|
|
8494
8873
|
this.requireTransaction("cannot rollback - no transaction is active");
|
|
@@ -8496,7 +8875,7 @@ var TransactionManager = class {
|
|
|
8496
8875
|
const index = this.findSavepoint(savepoint);
|
|
8497
8876
|
const snapshot2 = this.savepoints[index];
|
|
8498
8877
|
if (!snapshot2) throw new SqliteError(`no such savepoint: ${savepoint}`, "transaction", "SQLITE_ERROR");
|
|
8499
|
-
this.state.replaceWith(snapshot2.state);
|
|
8878
|
+
this.state.replaceWith(snapshot2.state, { adopt: true });
|
|
8500
8879
|
this.prng.setState(snapshot2.prngState);
|
|
8501
8880
|
this.savepoints.splice(index + 1);
|
|
8502
8881
|
return;
|
|
@@ -8506,22 +8885,26 @@ var TransactionManager = class {
|
|
|
8506
8885
|
if (!snapshot || prngState === null) {
|
|
8507
8886
|
throw new SqliteError("cannot rollback - no transaction is active", "transaction", "SQLITE_ERROR");
|
|
8508
8887
|
}
|
|
8509
|
-
this.state.replaceWith(snapshot);
|
|
8888
|
+
this.state.replaceWith(snapshot, { adopt: true });
|
|
8510
8889
|
this.prng.setState(prngState);
|
|
8511
8890
|
this.transactionSnapshot = null;
|
|
8512
8891
|
this.transactionPrngState = null;
|
|
8513
8892
|
this.savepoints = [];
|
|
8514
8893
|
this.startedBySavepoint = false;
|
|
8894
|
+
this.state.thawShared();
|
|
8515
8895
|
}
|
|
8516
8896
|
savepoint(name) {
|
|
8517
8897
|
if (!this.inTransaction) {
|
|
8518
|
-
this.
|
|
8898
|
+
this.state.freezeShared();
|
|
8899
|
+
this.transactionSnapshot = this.state.cloneShallow();
|
|
8519
8900
|
this.transactionPrngState = this.prng.getState();
|
|
8520
8901
|
this.startedBySavepoint = true;
|
|
8902
|
+
} else {
|
|
8903
|
+
this.state.freezeShared();
|
|
8521
8904
|
}
|
|
8522
8905
|
this.savepoints.push({
|
|
8523
8906
|
name,
|
|
8524
|
-
state: this.state.
|
|
8907
|
+
state: this.state.cloneShallow(),
|
|
8525
8908
|
prngState: this.prng.getState()
|
|
8526
8909
|
});
|
|
8527
8910
|
}
|
|
@@ -8533,6 +8916,7 @@ var TransactionManager = class {
|
|
|
8533
8916
|
this.transactionSnapshot = null;
|
|
8534
8917
|
this.transactionPrngState = null;
|
|
8535
8918
|
this.startedBySavepoint = false;
|
|
8919
|
+
this.state.thawShared();
|
|
8536
8920
|
}
|
|
8537
8921
|
}
|
|
8538
8922
|
findSavepoint(name) {
|
|
@@ -8597,16 +8981,14 @@ var ExecutionEnv = class {
|
|
|
8597
8981
|
}
|
|
8598
8982
|
getBoundParameter(name) {
|
|
8599
8983
|
if (typeof name === "number") {
|
|
8600
|
-
if (name < 1 || name > this.positional.length)
|
|
8601
|
-
throw new SqliteError(`binding parameter ${name} is not supplied`, "misuse");
|
|
8984
|
+
if (name < 1 || name > this.positional.length) return null;
|
|
8602
8985
|
return this.positional[name - 1];
|
|
8603
8986
|
}
|
|
8604
8987
|
const key = name.toLowerCase();
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
throw new SqliteError(`binding parameter :${name} is not supplied`, "misuse");
|
|
8988
|
+
if (!this.named.has(key)) {
|
|
8989
|
+
return null;
|
|
8608
8990
|
}
|
|
8609
|
-
return
|
|
8991
|
+
return this.named.get(key) ?? null;
|
|
8610
8992
|
}
|
|
8611
8993
|
setNamed(name, value) {
|
|
8612
8994
|
this.named.set(name.toLowerCase(), toSqlValue(value));
|
|
@@ -8694,7 +9076,7 @@ var ExecutionEnv = class {
|
|
|
8694
9076
|
const result = this.selectRunner(select, this, context);
|
|
8695
9077
|
return {
|
|
8696
9078
|
columns: result.columns,
|
|
8697
|
-
rows: result.values
|
|
9079
|
+
rows: result.values.length > 0 ? result.values.map((row2) => [...row2]) : result.rows.map((record) => result.columns.map((column) => record[column] ?? null))
|
|
8698
9080
|
};
|
|
8699
9081
|
},
|
|
8700
9082
|
matchFts: (table, column, query) => {
|
|
@@ -8721,14 +9103,30 @@ function toSqlValue(value) {
|
|
|
8721
9103
|
return canonicalizeNumber(value);
|
|
8722
9104
|
}
|
|
8723
9105
|
if (typeof value === "boolean") return value ? 1 : 0;
|
|
8724
|
-
if (value instanceof
|
|
9106
|
+
if (value instanceof SqlReal || value instanceof SqlJsonText) return value;
|
|
9107
|
+
if (value instanceof Uint8Array) {
|
|
9108
|
+
if (isSharedArrayBufferView(value)) {
|
|
9109
|
+
throw new SqliteError("cannot bind SharedArrayBuffer-backed buffers; copy into a Uint8Array first", "misuse");
|
|
9110
|
+
}
|
|
9111
|
+
return new Uint8Array(value);
|
|
9112
|
+
}
|
|
8725
9113
|
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
9114
|
+
if (typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer) {
|
|
9115
|
+
throw new SqliteError("cannot bind SharedArrayBuffer; copy into an ArrayBuffer or Uint8Array first", "misuse");
|
|
9116
|
+
}
|
|
9117
|
+
if (ArrayBuffer.isView(value)) {
|
|
9118
|
+
const kind = Object.prototype.toString.call(value).slice(8, -1);
|
|
9119
|
+
throw new SqliteError(`cannot bind ${kind}; only Uint8Array and ArrayBuffer are accepted as BLOB values`, "misuse");
|
|
9120
|
+
}
|
|
8726
9121
|
throw new SqliteError(`unsupported bind value: ${typeof value}`, "datatype_mismatch");
|
|
8727
9122
|
}
|
|
9123
|
+
function isSharedArrayBufferView(view) {
|
|
9124
|
+
return typeof SharedArrayBuffer !== "undefined" && view.buffer instanceof SharedArrayBuffer;
|
|
9125
|
+
}
|
|
8728
9126
|
|
|
8729
9127
|
// src/executor/result.ts
|
|
8730
9128
|
function emptyResult(changes = 0, lastInsertRowid = 0) {
|
|
8731
|
-
return { columns: [], rows: [], changes, lastInsertRowid };
|
|
9129
|
+
return { columns: [], rows: [], values: [], changes, lastInsertRowid };
|
|
8732
9130
|
}
|
|
8733
9131
|
function exportSqlValue(value) {
|
|
8734
9132
|
if (isSqlReal(value)) return value.value;
|
|
@@ -8740,7 +9138,7 @@ function valuesToResult(columns, values, changes = 0, lastInsertRowid = 0, optio
|
|
|
8740
9138
|
const keepValues = options?.keepValues !== false;
|
|
8741
9139
|
return {
|
|
8742
9140
|
columns,
|
|
8743
|
-
values: keepValues ? values.map((row) => row.map(exportSqlValue)) :
|
|
9141
|
+
values: keepValues ? values.map((row) => row.map(exportSqlValue)) : [],
|
|
8744
9142
|
rows: named ? values.map((row) => {
|
|
8745
9143
|
const object = {};
|
|
8746
9144
|
for (let index = 0; index < columns.length; index++) {
|
|
@@ -8753,7 +9151,10 @@ function valuesToResult(columns, values, changes = 0, lastInsertRowid = 0, optio
|
|
|
8753
9151
|
};
|
|
8754
9152
|
}
|
|
8755
9153
|
function resultValues(result) {
|
|
8756
|
-
|
|
9154
|
+
if (result.values.length > 0 || result.rows.length === 0) {
|
|
9155
|
+
return result.values.map((row) => [...row]);
|
|
9156
|
+
}
|
|
9157
|
+
return result.rows.map((row) => result.columns.map((column) => row[column] ?? null));
|
|
8757
9158
|
}
|
|
8758
9159
|
|
|
8759
9160
|
// src/executor/attach.ts
|
|
@@ -8935,6 +9336,44 @@ function buildSqliteMaster(state) {
|
|
|
8935
9336
|
return buildSchemaCatalog(state, "sqlite_master");
|
|
8936
9337
|
}
|
|
8937
9338
|
|
|
9339
|
+
// src/expressions/equals.ts
|
|
9340
|
+
function exprEquals(left, right) {
|
|
9341
|
+
if (left.type !== right.type) return false;
|
|
9342
|
+
switch (left.type) {
|
|
9343
|
+
case "null":
|
|
9344
|
+
return true;
|
|
9345
|
+
case "literal":
|
|
9346
|
+
return right.type === "literal" && valuesEqual(left.value, right.value);
|
|
9347
|
+
case "column":
|
|
9348
|
+
return right.type === "column" && (left.table ?? "").toLowerCase() === (right.table ?? "").toLowerCase() && left.name.toLowerCase() === right.name.toLowerCase();
|
|
9349
|
+
case "unary":
|
|
9350
|
+
return right.type === "unary" && left.op === right.op && exprEquals(left.expr, right.expr);
|
|
9351
|
+
case "binary":
|
|
9352
|
+
return right.type === "binary" && left.op === right.op && exprEquals(left.left, right.left) && exprEquals(left.right, right.right);
|
|
9353
|
+
case "function":
|
|
9354
|
+
return right.type === "function" && left.name.toLowerCase() === right.name.toLowerCase() && left.distinct === right.distinct && argsEqual(left.args, right.args);
|
|
9355
|
+
case "collate":
|
|
9356
|
+
return right.type === "collate" && left.collation.toLowerCase() === right.collation.toLowerCase() && exprEquals(left.expr, right.expr);
|
|
9357
|
+
case "cast":
|
|
9358
|
+
return right.type === "cast" && (left.typeName ?? "").toUpperCase() === (right.typeName ?? "").toUpperCase() && exprEquals(left.expr, right.expr);
|
|
9359
|
+
default:
|
|
9360
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
9361
|
+
}
|
|
9362
|
+
}
|
|
9363
|
+
function argsEqual(left, right) {
|
|
9364
|
+
if (left === "*" || right === "*") return left === right;
|
|
9365
|
+
if (left.length !== right.length) return false;
|
|
9366
|
+
return left.every((expr, index) => exprEquals(expr, right[index]));
|
|
9367
|
+
}
|
|
9368
|
+
function valuesEqual(left, right) {
|
|
9369
|
+
if (left === right) return true;
|
|
9370
|
+
if (left instanceof Uint8Array && right instanceof Uint8Array) {
|
|
9371
|
+
if (left.length !== right.length) return false;
|
|
9372
|
+
return left.every((byte, index) => byte === right[index]);
|
|
9373
|
+
}
|
|
9374
|
+
return false;
|
|
9375
|
+
}
|
|
9376
|
+
|
|
8938
9377
|
// src/planner/access.ts
|
|
8939
9378
|
function conjunctions(expr) {
|
|
8940
9379
|
if (expr.type === "binary" && expr.op === "AND") {
|
|
@@ -8955,6 +9394,16 @@ function equalityAgainstConst(expr) {
|
|
|
8955
9394
|
}
|
|
8956
9395
|
return null;
|
|
8957
9396
|
}
|
|
9397
|
+
function equalityAgainstExpr(expr) {
|
|
9398
|
+
if (expr.type !== "binary" || expr.op !== "=" && expr.op !== "==") return null;
|
|
9399
|
+
if (expr.left.type !== "column" && isRowIndependentExpr(expr.right)) {
|
|
9400
|
+
return { expr: expr.left, valueExpr: expr.right };
|
|
9401
|
+
}
|
|
9402
|
+
if (expr.right.type !== "column" && isRowIndependentExpr(expr.left)) {
|
|
9403
|
+
return { expr: expr.right, valueExpr: expr.left };
|
|
9404
|
+
}
|
|
9405
|
+
return null;
|
|
9406
|
+
}
|
|
8958
9407
|
function whereFullyCovered(where, coveredColumns, alias, tableName) {
|
|
8959
9408
|
for (const part of conjunctions(where)) {
|
|
8960
9409
|
const eq = equalityAgainstConst(part);
|
|
@@ -8991,8 +9440,8 @@ function lookupTableRows(from, where, env) {
|
|
|
8991
9440
|
} catch {
|
|
8992
9441
|
return null;
|
|
8993
9442
|
}
|
|
8994
|
-
const
|
|
8995
|
-
|
|
9443
|
+
const parts = conjunctions(where);
|
|
9444
|
+
const equalities = parts.map(equalityAgainstConst).filter((item) => item !== null).filter((item) => matchesTable(item.table, alias, table.name));
|
|
8996
9445
|
const evalConst = (expr) => {
|
|
8997
9446
|
try {
|
|
8998
9447
|
return evalExpr(expr, env.createEvalContext(null));
|
|
@@ -9006,7 +9455,6 @@ function lookupTableRows(from, where, env) {
|
|
|
9006
9455
|
if (value === void 0) continue;
|
|
9007
9456
|
resolved.push({ table: eq.table, column: eq.column, value });
|
|
9008
9457
|
}
|
|
9009
|
-
if (resolved.length === 0) return null;
|
|
9010
9458
|
const pk = table.integerPkColumn();
|
|
9011
9459
|
for (const eq of resolved) {
|
|
9012
9460
|
const col = eq.column.toLowerCase();
|
|
@@ -9026,17 +9474,40 @@ function lookupTableRows(from, where, env) {
|
|
|
9026
9474
|
let best = null;
|
|
9027
9475
|
for (const indexName of table.indexes) {
|
|
9028
9476
|
const index = db.indexes.get(indexName.toLowerCase());
|
|
9029
|
-
if (!index
|
|
9477
|
+
if (!index) continue;
|
|
9478
|
+
if (index.where && !parts.some((part) => exprEquals(part, index.where))) continue;
|
|
9479
|
+
if (index.columns.some((column) => column.expr)) {
|
|
9480
|
+
const prefix2 = [];
|
|
9481
|
+
for (const column of index.columns) {
|
|
9482
|
+
if (!column.expr) {
|
|
9483
|
+
const value2 = byColumn.get(column.name.toLowerCase());
|
|
9484
|
+
if (value2 === void 0) break;
|
|
9485
|
+
prefix2.push(normalizeForCollation(value2, column.collate ?? "BINARY"));
|
|
9486
|
+
continue;
|
|
9487
|
+
}
|
|
9488
|
+
const match = parts.map(equalityAgainstExpr).find((item) => item && exprEquals(item.expr, column.expr));
|
|
9489
|
+
if (!match) break;
|
|
9490
|
+
const value = evalConst(match.valueExpr);
|
|
9491
|
+
if (value === void 0) break;
|
|
9492
|
+
prefix2.push(normalizeForCollation(value, column.collate ?? "BINARY"));
|
|
9493
|
+
}
|
|
9494
|
+
if (prefix2.length === 0) continue;
|
|
9495
|
+
const rowids2 = prefix2.length === index.columns.length ? index.store.lookup(prefix2) : index.store.lookupPrefix(prefix2);
|
|
9496
|
+
best = { rowids: rowids2, columns: index.columns.map((column) => column.name.toLowerCase()) };
|
|
9497
|
+
if (index.unique || rowids2.length <= 1) break;
|
|
9498
|
+
continue;
|
|
9499
|
+
}
|
|
9030
9500
|
const prefix = [];
|
|
9031
9501
|
for (const column of index.columns) {
|
|
9032
9502
|
const value = byColumn.get(column.name.toLowerCase());
|
|
9033
9503
|
if (value === void 0) break;
|
|
9034
9504
|
prefix.push(normalizeForCollation(value, column.collate ?? "BINARY"));
|
|
9035
9505
|
}
|
|
9036
|
-
if (prefix.length
|
|
9037
|
-
const rowids = index.store.lookup(prefix);
|
|
9038
|
-
best = { rowids, columns: index.columns.map((column) => column.name.toLowerCase()) };
|
|
9039
|
-
if (index.unique
|
|
9506
|
+
if (prefix.length === 0) continue;
|
|
9507
|
+
const rowids = prefix.length === index.columns.length ? index.store.lookup(prefix) : index.store.lookupPrefix(prefix);
|
|
9508
|
+
best = { rowids, columns: index.columns.slice(0, prefix.length).map((column) => column.name.toLowerCase()) };
|
|
9509
|
+
if (index.unique && prefix.length === index.columns.length) break;
|
|
9510
|
+
if (rowids.length <= 1) break;
|
|
9040
9511
|
}
|
|
9041
9512
|
if (best) {
|
|
9042
9513
|
const rows = [];
|
|
@@ -9055,6 +9526,77 @@ function lookupTableRows(from, where, env) {
|
|
|
9055
9526
|
hashed.sort((a, b) => compareRowids3(a.rowid, b.rowid));
|
|
9056
9527
|
return { table, alias, rows: hashed, coveredColumns: /* @__PURE__ */ new Set([col]) };
|
|
9057
9528
|
}
|
|
9529
|
+
const range = rangeAgainstConst(where) ?? rangeFromBetween(where);
|
|
9530
|
+
if (range && matchesTable(range.table, alias, table.name)) {
|
|
9531
|
+
const value = evalConst(range.valueExpr);
|
|
9532
|
+
const value2 = range.valueExpr2 ? evalConst(range.valueExpr2) : void 0;
|
|
9533
|
+
if (value !== void 0) {
|
|
9534
|
+
for (const indexName of table.indexes) {
|
|
9535
|
+
const index = db.indexes.get(indexName.toLowerCase());
|
|
9536
|
+
if (!index || index.where || index.columns.length !== 1) continue;
|
|
9537
|
+
if (index.columns[0].expr) continue;
|
|
9538
|
+
if (index.columns[0].name.toLowerCase() !== range.column.toLowerCase()) continue;
|
|
9539
|
+
const bound = normalizeForCollation(value, index.columns[0].collate ?? "BINARY");
|
|
9540
|
+
const bound2 = value2 !== void 0 ? normalizeForCollation(value2, index.columns[0].collate ?? "BINARY") : void 0;
|
|
9541
|
+
const rowids = index.store.rangeLookup(range.op, bound, bound2);
|
|
9542
|
+
const rows = [];
|
|
9543
|
+
for (const rowid of rowids) {
|
|
9544
|
+
const row = table.get(rowid);
|
|
9545
|
+
if (row) rows.push(row);
|
|
9546
|
+
}
|
|
9547
|
+
return { table, alias, rows, coveredColumns: /* @__PURE__ */ new Set([range.column.toLowerCase()]) };
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
9551
|
+
return null;
|
|
9552
|
+
}
|
|
9553
|
+
function rangeAgainstConst(expr) {
|
|
9554
|
+
if (expr.type !== "binary") return null;
|
|
9555
|
+
const op = expr.op;
|
|
9556
|
+
if (op !== ">" && op !== ">=" && op !== "<" && op !== "<=") return null;
|
|
9557
|
+
if (expr.left.type === "column" && isRowIndependentExpr(expr.right)) {
|
|
9558
|
+
return { table: expr.left.table, column: expr.left.name, op, valueExpr: expr.right };
|
|
9559
|
+
}
|
|
9560
|
+
if (expr.right.type === "column" && isRowIndependentExpr(expr.left)) {
|
|
9561
|
+
const flipped = op === ">" ? "<" : op === ">=" ? "<=" : op === "<" ? ">" : ">=";
|
|
9562
|
+
return { table: expr.right.table, column: expr.right.name, op: flipped, valueExpr: expr.left };
|
|
9563
|
+
}
|
|
9564
|
+
return null;
|
|
9565
|
+
}
|
|
9566
|
+
function rangeFromBetween(expr) {
|
|
9567
|
+
if (expr.type !== "between" || expr.not || expr.expr.type !== "column") return null;
|
|
9568
|
+
return {
|
|
9569
|
+
table: expr.expr.table,
|
|
9570
|
+
column: expr.expr.name,
|
|
9571
|
+
op: "between",
|
|
9572
|
+
valueExpr: expr.lower,
|
|
9573
|
+
valueExpr2: expr.upper
|
|
9574
|
+
};
|
|
9575
|
+
}
|
|
9576
|
+
function tryIndexedOrder(from, order, env, limit) {
|
|
9577
|
+
if (from.type !== "table" || order.expr.type !== "column") return null;
|
|
9578
|
+
const alias = from.alias ?? from.name;
|
|
9579
|
+
const db = env.state.databaseForSchema(from.schema, from.schema ? `${from.schema}.${from.name}` : from.name);
|
|
9580
|
+
let table;
|
|
9581
|
+
try {
|
|
9582
|
+
table = db.getTable(from.name);
|
|
9583
|
+
} catch {
|
|
9584
|
+
return null;
|
|
9585
|
+
}
|
|
9586
|
+
const col = order.expr.name.toLowerCase();
|
|
9587
|
+
for (const indexName of table.indexes) {
|
|
9588
|
+
const index = db.indexes.get(indexName.toLowerCase());
|
|
9589
|
+
if (!index || index.where || index.columns.length !== 1 || index.columns[0].expr) continue;
|
|
9590
|
+
if (index.columns[0].name.toLowerCase() !== col) continue;
|
|
9591
|
+
const desc = order.dir === "DESC";
|
|
9592
|
+
const rows = [];
|
|
9593
|
+
for (const rowid of index.store.orderedRowids(desc)) {
|
|
9594
|
+
const row = table.get(rowid);
|
|
9595
|
+
if (row) rows.push(row);
|
|
9596
|
+
if (limit !== void 0 && rows.length >= limit) break;
|
|
9597
|
+
}
|
|
9598
|
+
return { table, alias, rows, coveredColumns: /* @__PURE__ */ new Set([col]) };
|
|
9599
|
+
}
|
|
9058
9600
|
return null;
|
|
9059
9601
|
}
|
|
9060
9602
|
function compareRowids3(left, right) {
|
|
@@ -9499,6 +10041,7 @@ function executeWith(stmt, env, parent) {
|
|
|
9499
10041
|
function executeSelectCore(stmt, env, parent) {
|
|
9500
10042
|
let scopes;
|
|
9501
10043
|
let skipWhere = false;
|
|
10044
|
+
let skipOrder = false;
|
|
9502
10045
|
if (stmt.from && stmt.where) {
|
|
9503
10046
|
const indexed = tryIndexedTableRows(stmt.from, stmt.where, env);
|
|
9504
10047
|
if (indexed) {
|
|
@@ -9507,6 +10050,28 @@ function executeSelectCore(stmt, env, parent) {
|
|
|
9507
10050
|
} else {
|
|
9508
10051
|
scopes = scanFrom(stmt.from, env, parent);
|
|
9509
10052
|
}
|
|
10053
|
+
} else if (stmt.from && stmt.orderBy.length === 1 && stmt.orderBy[0]) {
|
|
10054
|
+
let take;
|
|
10055
|
+
if (stmt.limit) {
|
|
10056
|
+
const ctx = env.createEvalContext(null, parent);
|
|
10057
|
+
const limitValue = toInteger(evalExpr(stmt.limit.limit, ctx));
|
|
10058
|
+
const offsetValue = stmt.limit.offset ? toInteger(evalExpr(stmt.limit.offset, ctx)) : 0;
|
|
10059
|
+
if (limitValue !== null && offsetValue !== null) {
|
|
10060
|
+
const offset = Math.max(0, Number(offsetValue));
|
|
10061
|
+
const limit = Number(limitValue);
|
|
10062
|
+
take = limit < 0 ? void 0 : offset + limit;
|
|
10063
|
+
}
|
|
10064
|
+
}
|
|
10065
|
+
const ordered = tryIndexedOrder(
|
|
10066
|
+
stmt.from,
|
|
10067
|
+
{ expr: stmt.orderBy[0].expr, dir: stmt.orderBy[0].dir ?? "ASC" },
|
|
10068
|
+
env,
|
|
10069
|
+
take
|
|
10070
|
+
);
|
|
10071
|
+
if (ordered) {
|
|
10072
|
+
scopes = scopesFromTableRows(ordered.table, ordered.rows, ordered.alias, env, parent);
|
|
10073
|
+
skipOrder = true;
|
|
10074
|
+
} else scopes = scanFrom(stmt.from, env, parent);
|
|
9510
10075
|
} else {
|
|
9511
10076
|
scopes = stmt.from ? scanFrom(stmt.from, env, parent) : [{ cells: [] }];
|
|
9512
10077
|
}
|
|
@@ -9580,7 +10145,7 @@ function executeSelectCore(stmt, env, parent) {
|
|
|
9580
10145
|
return true;
|
|
9581
10146
|
});
|
|
9582
10147
|
}
|
|
9583
|
-
if (stmt.orderBy.length > 0) {
|
|
10148
|
+
if (stmt.orderBy.length > 0 && !skipOrder) {
|
|
9584
10149
|
output.sort((left, right) => compareOutput(left, right, stmt.orderBy, columns, env, parent));
|
|
9585
10150
|
}
|
|
9586
10151
|
if (stmt.limit) {
|
|
@@ -10033,8 +10598,9 @@ function windowValue(expr, current, rows, env, parent, namedWindows) {
|
|
|
10033
10598
|
}
|
|
10034
10599
|
}
|
|
10035
10600
|
if (expr.func.type === "aggregate") {
|
|
10036
|
-
const [start2, end2] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd);
|
|
10037
|
-
|
|
10601
|
+
const [start2, end2] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd, orderKeys);
|
|
10602
|
+
const framed2 = frameRows(partition, start2, end2, index, orderKeys, spec.frame?.exclude ?? null);
|
|
10603
|
+
return aggregateValue(expr.func, framed2, env, parent);
|
|
10038
10604
|
}
|
|
10039
10605
|
const name = expr.func.name.toLowerCase();
|
|
10040
10606
|
const args = expr.func.args === "*" ? [] : expr.func.args;
|
|
@@ -10056,12 +10622,14 @@ function windowValue(expr, current, rows, env, parent, namedWindows) {
|
|
|
10056
10622
|
const target = name === "lag" ? index - offset : index + offset;
|
|
10057
10623
|
return values[target]?.[0] ?? evaluated[2] ?? null;
|
|
10058
10624
|
}
|
|
10059
|
-
const [start, end] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd);
|
|
10060
|
-
|
|
10061
|
-
|
|
10625
|
+
const [start, end] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd, orderKeys);
|
|
10626
|
+
const framed = frameRows(partition, start, end, index, orderKeys, spec.frame?.exclude ?? null);
|
|
10627
|
+
const framedValues = framed.map((row) => args.map((arg) => evalExpr(arg, env.createEvalContext(row, parent))));
|
|
10628
|
+
if (name === "first_value") return framedValues[0]?.[0] ?? null;
|
|
10629
|
+
if (name === "last_value") return framedValues[framedValues.length - 1]?.[0] ?? null;
|
|
10062
10630
|
if (name === "nth_value") {
|
|
10063
|
-
const target =
|
|
10064
|
-
return target >=
|
|
10631
|
+
const target = Number(toInteger(evaluated[1] ?? null) ?? 0) - 1;
|
|
10632
|
+
return target >= 0 && target < framedValues.length ? framedValues[target]?.[0] ?? null : null;
|
|
10065
10633
|
}
|
|
10066
10634
|
if (name === "ntile") {
|
|
10067
10635
|
const buckets = Math.max(1, Number(toInteger(evaluated[0] ?? 1) ?? 1));
|
|
@@ -10108,15 +10676,21 @@ function resolveWindow(spec, namedWindows, seen = /* @__PURE__ */ new Set()) {
|
|
|
10108
10676
|
ref: null
|
|
10109
10677
|
};
|
|
10110
10678
|
}
|
|
10111
|
-
function frameBounds(spec, index, length, ctx, defaultFrameEnd) {
|
|
10679
|
+
function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
10112
10680
|
if (!spec.frame) return [0, spec.orderBy.length > 0 ? defaultFrameEnd : Math.max(0, length - 1)];
|
|
10113
|
-
const
|
|
10681
|
+
const isRangeLike = spec.frame.type === "RANGE" || spec.frame.type === "GROUPS";
|
|
10682
|
+
let peerFirst = index;
|
|
10683
|
+
if (isRangeLike && spec.orderBy.length > 0) {
|
|
10684
|
+
while (peerFirst > 0 && rowsEqual(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
|
|
10685
|
+
}
|
|
10686
|
+
const bound = (item, isStart) => {
|
|
10114
10687
|
switch (item.kind) {
|
|
10115
10688
|
case "unbounded_preceding":
|
|
10116
10689
|
return 0;
|
|
10117
10690
|
case "unbounded_following":
|
|
10118
10691
|
return Math.max(0, length - 1);
|
|
10119
10692
|
case "current_row":
|
|
10693
|
+
if (isRangeLike) return isStart ? peerFirst : defaultFrameEnd;
|
|
10120
10694
|
return index;
|
|
10121
10695
|
case "preceding":
|
|
10122
10696
|
return Math.max(0, index - Number(toInteger(evalExpr(item.expr, ctx)) ?? 0));
|
|
@@ -10126,6 +10700,20 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd) {
|
|
|
10126
10700
|
};
|
|
10127
10701
|
return [bound(spec.frame.start, true), bound(spec.frame.end, false)];
|
|
10128
10702
|
}
|
|
10703
|
+
function frameRows(partition, start, end, index, orderKeys, exclude) {
|
|
10704
|
+
let peerFirst = index;
|
|
10705
|
+
let peerLast = index;
|
|
10706
|
+
while (peerFirst > 0 && rowsEqual(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
|
|
10707
|
+
while (peerLast + 1 < partition.length && rowsEqual(orderKeys[peerLast], orderKeys[peerLast + 1])) peerLast++;
|
|
10708
|
+
const rows = [];
|
|
10709
|
+
for (let i = Math.max(0, start); i <= end && i < partition.length; i++) {
|
|
10710
|
+
if (exclude === "current_row" && i === index) continue;
|
|
10711
|
+
if (exclude === "group" && i >= peerFirst && i <= peerLast) continue;
|
|
10712
|
+
if (exclude === "ties" && i >= peerFirst && i <= peerLast && i !== index) continue;
|
|
10713
|
+
rows.push(partition[i]);
|
|
10714
|
+
}
|
|
10715
|
+
return rows;
|
|
10716
|
+
}
|
|
10129
10717
|
function compareScopes(a, b, order, env, parent) {
|
|
10130
10718
|
for (const item of order) {
|
|
10131
10719
|
const left = evalExpr(item.expr, env.createEvalContext(a, parent));
|
|
@@ -10475,11 +11063,12 @@ function executeCreateIndex(stmt, env) {
|
|
|
10475
11063
|
value: row.values.get(normalizeColumnName(column.name)) ?? null
|
|
10476
11064
|
}))
|
|
10477
11065
|
});
|
|
10478
|
-
if (stmt.where &&
|
|
11066
|
+
if (stmt.where && isTruthySql(evalExpr(stmt.where, ctx)) !== true) continue;
|
|
10479
11067
|
index.store.insert(
|
|
10480
|
-
stmt.columns.map(
|
|
10481
|
-
(column)
|
|
10482
|
-
|
|
11068
|
+
stmt.columns.map((column) => {
|
|
11069
|
+
const raw = column.expr ? evalExpr(column.expr, ctx) : row.values.get(normalizeColumnName(column.name)) ?? null;
|
|
11070
|
+
return normalizeForCollation(raw, column.collate ?? "BINARY");
|
|
11071
|
+
}),
|
|
10483
11072
|
row.rowid,
|
|
10484
11073
|
stmt.unique
|
|
10485
11074
|
);
|
|
@@ -10491,7 +11080,7 @@ function executeCreateIndex(stmt, env) {
|
|
|
10491
11080
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
10492
11081
|
}
|
|
10493
11082
|
function executeAlterTable(stmt, env) {
|
|
10494
|
-
const table = env.state.
|
|
11083
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
10495
11084
|
const action = stmt.action;
|
|
10496
11085
|
if (action.kind === "rename_table") {
|
|
10497
11086
|
env.state.renameTable(stmt.table, action.newName);
|
|
@@ -10510,7 +11099,7 @@ function executeAlterTable(stmt, env) {
|
|
|
10510
11099
|
}
|
|
10511
11100
|
for (const constraint of table.constraints) renameConstraintColumn(constraint, action.oldName, action.newName);
|
|
10512
11101
|
for (const name of table.indexes) {
|
|
10513
|
-
const index = env.state.
|
|
11102
|
+
const index = env.state.getWritableIndex(name);
|
|
10514
11103
|
if (index) {
|
|
10515
11104
|
for (const indexed of index.columns)
|
|
10516
11105
|
if (indexed.name.toLowerCase() === action.oldName.toLowerCase()) indexed.name = action.newName;
|
|
@@ -10830,13 +11419,17 @@ function executeFtsDelete(table, rowids) {
|
|
|
10830
11419
|
}
|
|
10831
11420
|
|
|
10832
11421
|
// src/executor/dml.ts
|
|
11422
|
+
function storeColumnValue(table, column, value) {
|
|
11423
|
+
if (table.strict) return applyStrictValue(value, column.typeName ?? "", table.name, column.name);
|
|
11424
|
+
return applyAffinity(value, column.affinity);
|
|
11425
|
+
}
|
|
10833
11426
|
function executeInsert(stmt, env) {
|
|
10834
11427
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
10835
11428
|
return executeVirtualInsert(stmt, env);
|
|
10836
11429
|
}
|
|
10837
11430
|
const fast = tryFastInsert(stmt, env);
|
|
10838
11431
|
if (fast) return fast;
|
|
10839
|
-
const table = env.state.
|
|
11432
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
10840
11433
|
const columnNames = stmt.columns ?? table.columns.map((column) => column.name);
|
|
10841
11434
|
const rowidIndexes = columnNames.map((name, index) => isRowidName3(name) ? index : -1).filter((index) => index >= 0);
|
|
10842
11435
|
if (rowidIndexes.length > 1) throw new SqliteError("duplicate column name: rowid", "other");
|
|
@@ -10861,7 +11454,7 @@ function executeInsert(stmt, env) {
|
|
|
10861
11454
|
const column = table.columns[index];
|
|
10862
11455
|
const suppliedIndex = suppliedIndexes[index];
|
|
10863
11456
|
const value = suppliedIndex >= 0 ? source[suppliedIndex] ?? null : null;
|
|
10864
|
-
values.set(column.nameLower ?? column.name.toLowerCase(),
|
|
11457
|
+
values.set(column.nameLower ?? column.name.toLowerCase(), storeColumnValue(table, column, value));
|
|
10865
11458
|
}
|
|
10866
11459
|
last = table.insert({ values }, { prepared: true, skipValidate: true });
|
|
10867
11460
|
changes++;
|
|
@@ -10877,7 +11470,7 @@ function executeInsert(stmt, env) {
|
|
|
10877
11470
|
if (column.generated) continue;
|
|
10878
11471
|
const suppliedIndex = suppliedIndexes[table.columns.indexOf(column)] ?? -1;
|
|
10879
11472
|
const value = suppliedIndex >= 0 ? source[suppliedIndex] ?? null : column.defaultExpr ? evalExpr(column.defaultExpr, env.createEvalContext()) : null;
|
|
10880
|
-
values.set(column.nameLower ?? column.name.toLowerCase(),
|
|
11473
|
+
values.set(column.nameLower ?? column.name.toLowerCase(), storeColumnValue(table, column, value));
|
|
10881
11474
|
}
|
|
10882
11475
|
for (const column of table.columns) {
|
|
10883
11476
|
if (!column.generated) continue;
|
|
@@ -10901,7 +11494,7 @@ function executeInsert(stmt, env) {
|
|
|
10901
11494
|
})
|
|
10902
11495
|
});
|
|
10903
11496
|
const computed = evalExpr(column.generated.expr, genCtx);
|
|
10904
|
-
values.set(normalizeColumnName(column.name),
|
|
11497
|
+
values.set(normalizeColumnName(column.name), storeColumnValue(table, column, computed));
|
|
10905
11498
|
} else {
|
|
10906
11499
|
values.set(normalizeColumnName(column.name), null);
|
|
10907
11500
|
}
|
|
@@ -10910,7 +11503,7 @@ function executeInsert(stmt, env) {
|
|
|
10910
11503
|
const conflicts = conflictingRows(table, values, env);
|
|
10911
11504
|
if (conflicts.length > 0) {
|
|
10912
11505
|
if (stmt.upsert) {
|
|
10913
|
-
const targetConflicts =
|
|
11506
|
+
const targetConflicts = conflictsForUpsert(table, values, env, stmt.upsert);
|
|
10914
11507
|
if (targetConflicts.length === 0) {
|
|
10915
11508
|
throw new SqliteError(
|
|
10916
11509
|
`UNIQUE constraint failed: ${table.name}`,
|
|
@@ -11004,7 +11597,7 @@ function tryFastInsert(stmt, env) {
|
|
|
11004
11597
|
}
|
|
11005
11598
|
let table;
|
|
11006
11599
|
try {
|
|
11007
|
-
table = env.state.
|
|
11600
|
+
table = env.state.getWritableTable(plan.tableName);
|
|
11008
11601
|
} catch {
|
|
11009
11602
|
return null;
|
|
11010
11603
|
}
|
|
@@ -11055,7 +11648,7 @@ function executeUpdate(stmt, env) {
|
|
|
11055
11648
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
11056
11649
|
return executeVirtualUpdate(stmt, env);
|
|
11057
11650
|
}
|
|
11058
|
-
const table = env.state.
|
|
11651
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
11059
11652
|
const alias = stmt.alias ?? stmt.table;
|
|
11060
11653
|
const candidates = [];
|
|
11061
11654
|
if (stmt.from) {
|
|
@@ -11114,7 +11707,7 @@ function executeDelete(stmt, env) {
|
|
|
11114
11707
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
11115
11708
|
return executeVirtualDelete(stmt, env);
|
|
11116
11709
|
}
|
|
11117
|
-
const table = env.state.
|
|
11710
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
11118
11711
|
const selectedSource = stmt.where === null ? null : tryIndexedTableRows({ type: "table", schema: null, name: stmt.table, alias: stmt.alias }, stmt.where, env);
|
|
11119
11712
|
const selected = [...selectedSource ? selectedSource.rows : table.scan()].filter((row) => {
|
|
11120
11713
|
if (!stmt.where) return true;
|
|
@@ -11148,7 +11741,7 @@ function evaluateSet(items, table, ctx) {
|
|
|
11148
11741
|
if (item.columns.length === 1) {
|
|
11149
11742
|
const column = columnOf(table, item.columns[0]);
|
|
11150
11743
|
if (column.generated) throw new SqliteError(`cannot UPDATE generated column "${column.name}"`, "misuse");
|
|
11151
|
-
updates.set(normalizeColumnName(column.name), evalExpr(item.expr, ctx));
|
|
11744
|
+
updates.set(normalizeColumnName(column.name), storeColumnValue(table, column, evalExpr(item.expr, ctx)));
|
|
11152
11745
|
continue;
|
|
11153
11746
|
}
|
|
11154
11747
|
if (item.expr.type !== "row" || item.expr.values.length !== item.columns.length) {
|
|
@@ -11162,7 +11755,7 @@ function evaluateSet(items, table, ctx) {
|
|
|
11162
11755
|
if (column.generated) throw new SqliteError(`cannot UPDATE generated column "${column.name}"`, "misuse");
|
|
11163
11756
|
updates.set(
|
|
11164
11757
|
normalizeColumnName(column.name),
|
|
11165
|
-
evalExpr(item.expr.type === "row" ? item.expr.values[index] : item.expr, ctx)
|
|
11758
|
+
storeColumnValue(table, column, evalExpr(item.expr.type === "row" ? item.expr.values[index] : item.expr, ctx))
|
|
11166
11759
|
);
|
|
11167
11760
|
});
|
|
11168
11761
|
}
|
|
@@ -11189,7 +11782,7 @@ function updateOne(table, row, updates, env) {
|
|
|
11189
11782
|
});
|
|
11190
11783
|
merged.set(
|
|
11191
11784
|
normalizeColumnName(column.name),
|
|
11192
|
-
|
|
11785
|
+
storeColumnValue(table, column, evalExpr(column.generated.expr, genCtx))
|
|
11193
11786
|
);
|
|
11194
11787
|
}
|
|
11195
11788
|
after = table.update(row.rowid, merged);
|
|
@@ -11220,20 +11813,23 @@ function validateRow(table, row, env) {
|
|
|
11220
11813
|
);
|
|
11221
11814
|
}
|
|
11222
11815
|
function addIndexes(table, row, env) {
|
|
11223
|
-
const
|
|
11816
|
+
const db = env.state.databaseForTable(table);
|
|
11224
11817
|
for (const name of table.indexes) {
|
|
11225
|
-
const
|
|
11226
|
-
if (!
|
|
11818
|
+
const found = db.indexes.get(name.toLowerCase());
|
|
11819
|
+
if (!found) continue;
|
|
11820
|
+
const index = db.ensureWritableIndex(found);
|
|
11227
11821
|
if (index.where && isTruthySql(evalExpr(index.where, env.createEvalContext(scopeFor(table, row, table.name, env)))) !== true)
|
|
11228
11822
|
continue;
|
|
11229
|
-
index.store.insert(indexValues(index.columns, row), row.rowid, index.unique);
|
|
11823
|
+
index.store.insert(indexValues(index.columns, row, env, table), row.rowid, index.unique);
|
|
11230
11824
|
}
|
|
11231
11825
|
}
|
|
11232
11826
|
function removeIndexes(table, row, env) {
|
|
11233
|
-
const
|
|
11827
|
+
const db = env.state.databaseForTable(table);
|
|
11234
11828
|
for (const name of table.indexes) {
|
|
11235
|
-
const
|
|
11236
|
-
if (
|
|
11829
|
+
const found = db.indexes.get(name.toLowerCase());
|
|
11830
|
+
if (!found) continue;
|
|
11831
|
+
const index = db.ensureWritableIndex(found);
|
|
11832
|
+
index.store.remove(indexValues(index.columns, row, env, table), row.rowid);
|
|
11237
11833
|
}
|
|
11238
11834
|
}
|
|
11239
11835
|
function removeOne(table, row, env) {
|
|
@@ -11266,6 +11862,27 @@ function conflictsMatchingTarget(table, values, env, targetColumns) {
|
|
|
11266
11862
|
}
|
|
11267
11863
|
return conflictsForSets(table, values, matchingSets, env);
|
|
11268
11864
|
}
|
|
11865
|
+
function conflictsForUpsert(table, values, env, upsert) {
|
|
11866
|
+
if (!upsert.targetColumns) return conflictingRows(table, values, env);
|
|
11867
|
+
const exprs = upsert.targetExprs;
|
|
11868
|
+
const hasExpr = exprs?.some((expr) => expr.type !== "column") ?? false;
|
|
11869
|
+
if (hasExpr && exprs) {
|
|
11870
|
+
const db = env.state.databaseForTable(table);
|
|
11871
|
+
for (const indexName of table.indexes) {
|
|
11872
|
+
const index = db.indexes.get(indexName.toLowerCase());
|
|
11873
|
+
if (!index?.unique || index.columns.length !== exprs.length) continue;
|
|
11874
|
+
const matches = index.columns.every((column, i) => {
|
|
11875
|
+
const expr = exprs[i];
|
|
11876
|
+
if (column.expr) return exprEquals(column.expr, expr);
|
|
11877
|
+
return expr.type === "column" && expr.name.toLowerCase() === column.name.toLowerCase();
|
|
11878
|
+
});
|
|
11879
|
+
if (!matches) continue;
|
|
11880
|
+
if (upsert.targetWhere && index.where && !exprEquals(upsert.targetWhere, index.where)) continue;
|
|
11881
|
+
return conflictsForSets(table, values, [index.columns], env);
|
|
11882
|
+
}
|
|
11883
|
+
}
|
|
11884
|
+
return conflictsMatchingTarget(table, values, env, upsert.targetColumns.filter(Boolean));
|
|
11885
|
+
}
|
|
11269
11886
|
function uniqueColumnSets(table, env) {
|
|
11270
11887
|
const sets = [];
|
|
11271
11888
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -11277,7 +11894,7 @@ function uniqueColumnSets(table, env) {
|
|
|
11277
11894
|
};
|
|
11278
11895
|
for (const indexName of table.indexes) {
|
|
11279
11896
|
const index = env.state.databaseForTable(table).indexes.get(indexName.toLowerCase());
|
|
11280
|
-
if (index?.unique
|
|
11897
|
+
if (index?.unique) remember(index.columns);
|
|
11281
11898
|
}
|
|
11282
11899
|
const pk = table.integerPkColumn();
|
|
11283
11900
|
if (pk) remember([{ name: pk.name, collate: pk.collate, order: null }]);
|
|
@@ -11303,9 +11920,7 @@ function conflictsForSets(table, values, sets, env) {
|
|
|
11303
11920
|
};
|
|
11304
11921
|
const db = env.state.databaseForTable(table);
|
|
11305
11922
|
for (const columns of sets) {
|
|
11306
|
-
const desired = columns
|
|
11307
|
-
(column) => normalizeForCollation(values.get(normalizeColumnName(column.name)) ?? null, column.collate ?? "BINARY")
|
|
11308
|
-
);
|
|
11923
|
+
const desired = indexValuesFromMap(columns, values, table, env);
|
|
11309
11924
|
if (desired.some((value) => value === null)) continue;
|
|
11310
11925
|
if (columns.length === 1) {
|
|
11311
11926
|
const pk = table.integerPkColumn();
|
|
@@ -11318,12 +11933,13 @@ function conflictsForSets(table, values, sets, env) {
|
|
|
11318
11933
|
let usedIndex = false;
|
|
11319
11934
|
for (const indexName of table.indexes) {
|
|
11320
11935
|
const index = db.indexes.get(indexName.toLowerCase());
|
|
11321
|
-
if (!index?.unique
|
|
11936
|
+
if (!index?.unique) continue;
|
|
11322
11937
|
if (index.columns.length !== columns.length) continue;
|
|
11323
11938
|
if (!index.columns.every((column, i) => normalizeColumnName(column.name) === normalizeColumnName(columns[i].name))) {
|
|
11324
11939
|
continue;
|
|
11325
11940
|
}
|
|
11326
11941
|
usedIndex = true;
|
|
11942
|
+
if (index.where && !indexWhereMatches(index.where, values, table, env)) continue;
|
|
11327
11943
|
for (const rowid of index.store.lookup(desired)) {
|
|
11328
11944
|
const row = table.get(rowid);
|
|
11329
11945
|
if (row) push(row);
|
|
@@ -11332,36 +11948,63 @@ function conflictsForSets(table, values, sets, env) {
|
|
|
11332
11948
|
}
|
|
11333
11949
|
if (usedIndex) continue;
|
|
11334
11950
|
for (const row of table.scan()) {
|
|
11335
|
-
const existing = indexValues(columns, row);
|
|
11951
|
+
const existing = indexValues(columns, row, env, table);
|
|
11336
11952
|
if (desired.every((value, index) => compareSql(value, existing[index] ?? null) === 0)) push(row);
|
|
11337
11953
|
}
|
|
11338
11954
|
}
|
|
11339
11955
|
return found;
|
|
11340
11956
|
}
|
|
11341
|
-
function indexValues(columns, row) {
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11957
|
+
function indexValues(columns, row, env, table) {
|
|
11958
|
+
if (!env || !table) {
|
|
11959
|
+
return columns.map(
|
|
11960
|
+
(column) => normalizeForCollation(row.values.get(normalizeColumnName(column.name)) ?? null, column.collate ?? "BINARY")
|
|
11961
|
+
);
|
|
11962
|
+
}
|
|
11963
|
+
const ctx = env.createEvalContext(scopeFor(table, row, table.name, env));
|
|
11964
|
+
return columns.map((column) => {
|
|
11965
|
+
const raw = column.expr ? evalExpr(column.expr, ctx) : row.values.get(normalizeColumnName(column.name)) ?? null;
|
|
11966
|
+
return normalizeForCollation(raw, column.collate ?? "BINARY");
|
|
11967
|
+
});
|
|
11968
|
+
}
|
|
11969
|
+
function indexValuesFromMap(columns, values, table, env) {
|
|
11970
|
+
const fake = { rowid: 0, values };
|
|
11971
|
+
return indexValues(columns, fake, env, table);
|
|
11972
|
+
}
|
|
11973
|
+
function indexWhereMatches(where, values, table, env) {
|
|
11974
|
+
const fake = { rowid: 0, values };
|
|
11975
|
+
return isTruthySql(evalExpr(where, env.createEvalContext(scopeFor(table, fake, table.name, env)))) === true;
|
|
11345
11976
|
}
|
|
11346
11977
|
function checkForeignKeys(table, row, env) {
|
|
11347
11978
|
if (!env.state.foreignKeysEnabled) return;
|
|
11348
|
-
const references = [];
|
|
11349
11979
|
for (const constraint of table.constraints) {
|
|
11350
|
-
if (constraint.type
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
|
|
11354
|
-
|
|
11355
|
-
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11980
|
+
if (constraint.type !== "foreign_key") continue;
|
|
11981
|
+
if (fkIsDeferred(constraint, env)) continue;
|
|
11982
|
+
assertForeignKeySatisfied(row, constraint, env);
|
|
11983
|
+
}
|
|
11984
|
+
}
|
|
11985
|
+
function fkIsDeferred(constraint, env) {
|
|
11986
|
+
return env.transactions.inTransaction && constraint.initiallyDeferred;
|
|
11987
|
+
}
|
|
11988
|
+
function assertForeignKeySatisfied(row, constraint, env) {
|
|
11989
|
+
const values = constraint.columns.map((name) => row.values.get(normalizeColumnName(name)) ?? null);
|
|
11990
|
+
if (values.some((value) => value === null)) return;
|
|
11991
|
+
const parent = env.state.getTable(constraint.refTable);
|
|
11992
|
+
const parentColumns = constraint.refColumns ?? parent.columns.filter((column) => column.primaryKey).map((column) => column.name);
|
|
11993
|
+
if (![...parent.scan()].some(
|
|
11994
|
+
(candidate) => values.every((value, index) => {
|
|
11995
|
+
const parentColumn = parentColumns[index];
|
|
11996
|
+
return parentColumn !== void 0 && compareSql(value, candidate.values.get(normalizeColumnName(parentColumn)) ?? null) === 0;
|
|
11997
|
+
})
|
|
11998
|
+
)) {
|
|
11999
|
+
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12000
|
+
}
|
|
12001
|
+
}
|
|
12002
|
+
function checkDeferredForeignKeys(env) {
|
|
12003
|
+
if (!env.state.foreignKeysEnabled) return;
|
|
12004
|
+
for (const table of env.state.tables.values()) {
|
|
12005
|
+
for (const constraint of table.constraints) {
|
|
12006
|
+
if (constraint.type !== "foreign_key" || !constraint.initiallyDeferred) continue;
|
|
12007
|
+
for (const row of table.scan()) assertForeignKeySatisfied(row, constraint, env);
|
|
11365
12008
|
}
|
|
11366
12009
|
}
|
|
11367
12010
|
}
|
|
@@ -11393,8 +12036,9 @@ function applyReferentialDelete(parent, row, env) {
|
|
|
11393
12036
|
} else if (constraint.onDelete === "SET DEFAULT") {
|
|
11394
12037
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
11395
12038
|
changes += 1 + updated.cascaded;
|
|
11396
|
-
} else
|
|
12039
|
+
} else if (!fkIsDeferred(constraint, env)) {
|
|
11397
12040
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12041
|
+
}
|
|
11398
12042
|
}
|
|
11399
12043
|
}
|
|
11400
12044
|
}
|
|
@@ -11435,7 +12079,7 @@ function applyReferentialUpdate(parent, before, after, env) {
|
|
|
11435
12079
|
} else if (constraint.onUpdate === "SET DEFAULT") {
|
|
11436
12080
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
11437
12081
|
changes += 1 + updated.cascaded;
|
|
11438
|
-
} else {
|
|
12082
|
+
} else if (!fkIsDeferred(constraint, env)) {
|
|
11439
12083
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
11440
12084
|
}
|
|
11441
12085
|
}
|
|
@@ -11901,6 +12545,7 @@ function executeStatement(stmt, env) {
|
|
|
11901
12545
|
env.transactions.begin();
|
|
11902
12546
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11903
12547
|
case "commit":
|
|
12548
|
+
checkDeferredForeignKeys(env);
|
|
11904
12549
|
env.transactions.commit();
|
|
11905
12550
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11906
12551
|
case "rollback":
|
|
@@ -11947,7 +12592,8 @@ function executeAnalyze(stmt, env) {
|
|
|
11947
12592
|
],
|
|
11948
12593
|
constraints: [],
|
|
11949
12594
|
asSelect: null,
|
|
11950
|
-
withoutRowid: false
|
|
12595
|
+
withoutRowid: false,
|
|
12596
|
+
strict: false
|
|
11951
12597
|
},
|
|
11952
12598
|
"CREATE TABLE sqlite_stat1(tbl,idx,stat)"
|
|
11953
12599
|
);
|
|
@@ -11990,51 +12636,88 @@ function executeAnalyze(stmt, env) {
|
|
|
11990
12636
|
}
|
|
11991
12637
|
|
|
11992
12638
|
// src/api/statement.ts
|
|
11993
|
-
var Statement = class {
|
|
12639
|
+
var Statement = class _Statement {
|
|
11994
12640
|
constructor(database, sql, statements) {
|
|
11995
12641
|
this.database = database;
|
|
11996
12642
|
this.sql = sql;
|
|
11997
12643
|
this.statements = statements;
|
|
12644
|
+
this.schemaVersion = database.state.schemaVersion;
|
|
11998
12645
|
}
|
|
11999
12646
|
database;
|
|
12000
12647
|
sql;
|
|
12001
|
-
statements;
|
|
12002
|
-
bound = [];
|
|
12003
12648
|
namedPlan = null;
|
|
12004
12649
|
env = null;
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12650
|
+
statements;
|
|
12651
|
+
schemaVersion;
|
|
12652
|
+
/**
|
|
12653
|
+
* Construct a {@link Statement} for {@link Database.prepare} / {@link Database.exec}.
|
|
12654
|
+
* @internal
|
|
12655
|
+
*/
|
|
12656
|
+
static create(database, sql, statements) {
|
|
12657
|
+
return new _Statement(database, sql, statements);
|
|
12008
12658
|
}
|
|
12659
|
+
/**
|
|
12660
|
+
* Execute for side effects (INSERT / UPDATE / DELETE / DDL).
|
|
12661
|
+
*
|
|
12662
|
+
* @param params - Bind values for this call only.
|
|
12663
|
+
* @returns Mutation counters for this execution.
|
|
12664
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12665
|
+
*/
|
|
12009
12666
|
run(...params) {
|
|
12010
|
-
const result = this.execute(params
|
|
12667
|
+
const result = this.execute(params, { named: false });
|
|
12011
12668
|
return { changes: result.changes, lastInsertRowid: result.lastInsertRowid };
|
|
12012
12669
|
}
|
|
12670
|
+
/**
|
|
12671
|
+
* Execute and return every result row as an object keyed by column name.
|
|
12672
|
+
*
|
|
12673
|
+
* @typeParam T - Row shape. Defaults to {@link QueryRow}.
|
|
12674
|
+
* @param params - Bind values for this call only.
|
|
12675
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12676
|
+
*/
|
|
12013
12677
|
all(...params) {
|
|
12014
|
-
return this.execute(params
|
|
12678
|
+
return this.execute(params, { named: true }).rows;
|
|
12015
12679
|
}
|
|
12016
|
-
/**
|
|
12680
|
+
/**
|
|
12681
|
+
* Execute and return the full {@link ResultSet}, including column names.
|
|
12682
|
+
*
|
|
12683
|
+
* Use this when you need metadata for an empty result (column names with zero rows).
|
|
12684
|
+
* {@link all} only returns row objects.
|
|
12685
|
+
*
|
|
12686
|
+
* @param params - Bind values for this call only.
|
|
12687
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12688
|
+
*/
|
|
12017
12689
|
result(...params) {
|
|
12018
|
-
return this.execute(params
|
|
12690
|
+
return this.execute(params, { named: true });
|
|
12019
12691
|
}
|
|
12692
|
+
/**
|
|
12693
|
+
* Execute and return the first row, or `undefined` if there are no rows.
|
|
12694
|
+
*
|
|
12695
|
+
* @typeParam T - Row shape. Defaults to {@link QueryRow}.
|
|
12696
|
+
* @param params - Bind values for this call only.
|
|
12697
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12698
|
+
*/
|
|
12020
12699
|
get(...params) {
|
|
12021
|
-
return this.execute(params
|
|
12700
|
+
return this.execute(params, { named: true, maxRows: 1 }).rows[0];
|
|
12022
12701
|
}
|
|
12023
12702
|
execute(params, options) {
|
|
12024
12703
|
this.database.assertOpen();
|
|
12704
|
+
this.reprepareIfSchemaChanged();
|
|
12025
12705
|
if (this.statements.length === 0) throw new SqliteError("empty statement", "misuse");
|
|
12706
|
+
this.namedPlan ??= planNamedParameters(this.sql);
|
|
12707
|
+
const expected = this.namedPlan.expectedCount;
|
|
12708
|
+
if (params.length > 0 && params.length !== expected) {
|
|
12709
|
+
throw new SqliteError(`SQLite query expected ${expected} values, received ${params.length}`, "misuse");
|
|
12710
|
+
}
|
|
12026
12711
|
const env = this.obtainEnv(params);
|
|
12027
12712
|
env.maxRows = options?.maxRows ?? Number.POSITIVE_INFINITY;
|
|
12028
12713
|
env.includeNamedRows = options?.named !== false;
|
|
12029
12714
|
env.includeValues = true;
|
|
12030
12715
|
this.bindNamed(env, params);
|
|
12031
12716
|
let result;
|
|
12032
|
-
let lastQuery;
|
|
12033
12717
|
for (const statement of this.statements) {
|
|
12034
12718
|
result = executeStatement(statement, env);
|
|
12035
|
-
if (result.columns.length > 0) lastQuery = result;
|
|
12036
12719
|
}
|
|
12037
|
-
return
|
|
12720
|
+
return result;
|
|
12038
12721
|
}
|
|
12039
12722
|
obtainEnv(params) {
|
|
12040
12723
|
if (this.env) {
|
|
@@ -12051,6 +12734,13 @@ var Statement = class {
|
|
|
12051
12734
|
});
|
|
12052
12735
|
return this.env;
|
|
12053
12736
|
}
|
|
12737
|
+
reprepareIfSchemaChanged() {
|
|
12738
|
+
if (this.schemaVersion === this.database.state.schemaVersion) return;
|
|
12739
|
+
this.statements = parse(this.sql);
|
|
12740
|
+
this.env = null;
|
|
12741
|
+
this.namedPlan = null;
|
|
12742
|
+
this.schemaVersion = this.database.state.schemaVersion;
|
|
12743
|
+
}
|
|
12054
12744
|
bindNamed(env, params) {
|
|
12055
12745
|
this.namedPlan ??= planNamedParameters(this.sql);
|
|
12056
12746
|
for (const item of this.namedPlan.named) {
|
|
@@ -12077,61 +12767,129 @@ function planNamedParameters(sql) {
|
|
|
12077
12767
|
}
|
|
12078
12768
|
}
|
|
12079
12769
|
}
|
|
12080
|
-
return { named };
|
|
12770
|
+
return { named, expectedCount: nextSlot - 1 };
|
|
12081
12771
|
}
|
|
12082
12772
|
|
|
12083
12773
|
// src/api/database.ts
|
|
12084
12774
|
var Database = class {
|
|
12775
|
+
/** @internal Engine catalog, tables, and mutation counters. */
|
|
12085
12776
|
state = new DatabaseState();
|
|
12777
|
+
/** Seed used to construct the PRNG. */
|
|
12086
12778
|
seed;
|
|
12779
|
+
/**
|
|
12780
|
+
* PRNG backing `random()` / `randomblob()` and related builtins.
|
|
12781
|
+
* Prefer passing `seed` to the constructor.
|
|
12782
|
+
* @internal
|
|
12783
|
+
*/
|
|
12087
12784
|
prng;
|
|
12785
|
+
/**
|
|
12786
|
+
* Clock used by `date('now')` / `datetime('now')` / `CURRENT_TIMESTAMP`.
|
|
12787
|
+
* Prefer passing `now` to the constructor.
|
|
12788
|
+
* @internal
|
|
12789
|
+
*/
|
|
12088
12790
|
now;
|
|
12791
|
+
/** @internal Transaction / savepoint manager. */
|
|
12089
12792
|
transactions;
|
|
12090
12793
|
closed = false;
|
|
12091
12794
|
transactionSequence = 0;
|
|
12795
|
+
/** Depth of active {@link transaction} callbacks (not SQL BEGIN). */
|
|
12796
|
+
apiTransactionDepth = 0;
|
|
12797
|
+
/**
|
|
12798
|
+
* Create an empty in-memory database.
|
|
12799
|
+
*
|
|
12800
|
+
* @param options - Determinism knobs. See {@link DatabaseOptions}.
|
|
12801
|
+
*/
|
|
12092
12802
|
constructor(options = {}) {
|
|
12093
12803
|
this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
|
|
12094
|
-
this.prng =
|
|
12804
|
+
this.prng = new Prng(this.seed);
|
|
12095
12805
|
this.now = resolveClock(options.now);
|
|
12096
12806
|
this.transactions = new TransactionManager(this.state, this.prng);
|
|
12097
12807
|
}
|
|
12098
|
-
|
|
12808
|
+
/**
|
|
12809
|
+
* Execute SQL for its side effects (DDL/DML). Multiple statements are allowed.
|
|
12810
|
+
*
|
|
12811
|
+
* Does not accept bind parameters — use {@link prepare} or {@link query}.
|
|
12812
|
+
*
|
|
12813
|
+
* @param sql - SQL to run (semicolon-separated statements are ok).
|
|
12814
|
+
* @throws {SqliteError} If the database is closed, extra arguments are passed, or the SQL fails.
|
|
12815
|
+
*/
|
|
12816
|
+
exec(sql) {
|
|
12099
12817
|
this.assertOpen();
|
|
12100
|
-
|
|
12818
|
+
if (arguments.length > 1) {
|
|
12819
|
+
throw new SqliteError("exec() does not accept parameters; use prepare() or query()", "misuse");
|
|
12820
|
+
}
|
|
12821
|
+
Statement.create(this, sql, parse(sql)).run();
|
|
12101
12822
|
}
|
|
12823
|
+
/**
|
|
12824
|
+
* Execute a single-statement query and return all rows as objects keyed by column name.
|
|
12825
|
+
*
|
|
12826
|
+
* @typeParam T - Row shape. Defaults to {@link QueryRow}.
|
|
12827
|
+
* @param sql - A single SQL statement (trailing `;` is fine).
|
|
12828
|
+
* @param params - Bound parameters for `?` / `:name` placeholders.
|
|
12829
|
+
* @returns All result rows.
|
|
12830
|
+
* @throws {SqliteError} If the database is closed, `sql` is not a single statement, or execution fails.
|
|
12831
|
+
*/
|
|
12102
12832
|
query(sql, params = []) {
|
|
12103
12833
|
this.assertOpen();
|
|
12104
|
-
return
|
|
12834
|
+
return this.prepareSingle(sql).all(...params);
|
|
12105
12835
|
}
|
|
12836
|
+
/**
|
|
12837
|
+
* Compile a single SQL statement into a reusable {@link Statement}.
|
|
12838
|
+
*
|
|
12839
|
+
* @param sql - A single SQL statement (trailing `;` is fine). Multi-statement scripts are rejected.
|
|
12840
|
+
* @throws {SqliteError} If the database is closed or `sql` cannot be prepared as one statement.
|
|
12841
|
+
*/
|
|
12106
12842
|
prepare(sql) {
|
|
12107
12843
|
this.assertOpen();
|
|
12108
|
-
return
|
|
12844
|
+
return this.prepareSingle(sql);
|
|
12109
12845
|
}
|
|
12846
|
+
/**
|
|
12847
|
+
* Run `fn` inside a transaction. Commits on success; rolls back if `fn` throws.
|
|
12848
|
+
*
|
|
12849
|
+
* Nested calls use SAVEPOINTs so an inner failure does not abort the outer
|
|
12850
|
+
* transaction. Calling {@link close} from inside `fn` throws `misuse`.
|
|
12851
|
+
*
|
|
12852
|
+
* @param fn - Work to run while the transaction is open.
|
|
12853
|
+
* @returns The value returned by `fn`.
|
|
12854
|
+
* @throws {SqliteError} If the database is closed. Re-throws whatever `fn` throws after rollback.
|
|
12855
|
+
*/
|
|
12110
12856
|
transaction(fn) {
|
|
12111
12857
|
this.assertOpen();
|
|
12112
|
-
|
|
12113
|
-
|
|
12858
|
+
this.apiTransactionDepth++;
|
|
12859
|
+
try {
|
|
12860
|
+
if (!this.transactions.inTransaction) {
|
|
12861
|
+
this.transactions.begin();
|
|
12862
|
+
try {
|
|
12863
|
+
const value = fn();
|
|
12864
|
+
this.transactions.commit();
|
|
12865
|
+
return value;
|
|
12866
|
+
} catch (error) {
|
|
12867
|
+
this.transactions.rollback();
|
|
12868
|
+
throw error;
|
|
12869
|
+
}
|
|
12870
|
+
}
|
|
12871
|
+
const name = `__api_transaction_${++this.transactionSequence}`;
|
|
12872
|
+
this.transactions.savepoint(name);
|
|
12114
12873
|
try {
|
|
12115
12874
|
const value = fn();
|
|
12116
|
-
this.transactions.
|
|
12875
|
+
this.transactions.release(name);
|
|
12117
12876
|
return value;
|
|
12118
12877
|
} catch (error) {
|
|
12119
|
-
this.transactions.rollback();
|
|
12878
|
+
this.transactions.rollback(name);
|
|
12879
|
+
this.transactions.release(name);
|
|
12120
12880
|
throw error;
|
|
12121
12881
|
}
|
|
12122
|
-
}
|
|
12123
|
-
|
|
12124
|
-
this.transactions.savepoint(name);
|
|
12125
|
-
try {
|
|
12126
|
-
const value = fn();
|
|
12127
|
-
this.transactions.release(name);
|
|
12128
|
-
return value;
|
|
12129
|
-
} catch (error) {
|
|
12130
|
-
this.transactions.rollback(name);
|
|
12131
|
-
this.transactions.release(name);
|
|
12132
|
-
throw error;
|
|
12882
|
+
} finally {
|
|
12883
|
+
this.apiTransactionDepth--;
|
|
12133
12884
|
}
|
|
12134
12885
|
}
|
|
12886
|
+
/**
|
|
12887
|
+
* Serialize schema, rows, PRNG state, and clock into a custom snapshot blob.
|
|
12888
|
+
*
|
|
12889
|
+
* This is not a `.sqlite` file. Restore it with {@link restore}.
|
|
12890
|
+
*
|
|
12891
|
+
* @throws {SqliteError} If the database is closed.
|
|
12892
|
+
*/
|
|
12135
12893
|
snapshot() {
|
|
12136
12894
|
this.assertOpen();
|
|
12137
12895
|
return encodeDatabaseState(this.state, {
|
|
@@ -12139,6 +12897,16 @@ var Database = class {
|
|
|
12139
12897
|
nowMs: this.now().getTime()
|
|
12140
12898
|
});
|
|
12141
12899
|
}
|
|
12900
|
+
/**
|
|
12901
|
+
* Replace this database's contents with a blob from {@link snapshot}.
|
|
12902
|
+
*
|
|
12903
|
+
* Restores PRNG state and the clock when the snapshot includes them (v2).
|
|
12904
|
+
* Newer library versions can restore older snapshots; older libraries cannot
|
|
12905
|
+
* restore newer format versions.
|
|
12906
|
+
*
|
|
12907
|
+
* @param snapshot - Bytes previously returned by {@link snapshot}.
|
|
12908
|
+
* @throws {SqliteError} If the database is closed, a transaction is open, or the blob is invalid.
|
|
12909
|
+
*/
|
|
12142
12910
|
restore(snapshot) {
|
|
12143
12911
|
this.assertOpen();
|
|
12144
12912
|
if (this.transactions.inTransaction) throw new SqliteError("cannot restore during a transaction", "transaction");
|
|
@@ -12150,55 +12918,70 @@ var Database = class {
|
|
|
12150
12918
|
this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
12151
12919
|
}
|
|
12152
12920
|
}
|
|
12921
|
+
/**
|
|
12922
|
+
* Close the database. Further SQL throws {@link SqliteError}. Idempotent.
|
|
12923
|
+
*
|
|
12924
|
+
* Rolls back an open SQL transaction, if any. Throws if called from inside
|
|
12925
|
+
* a {@link transaction} callback.
|
|
12926
|
+
*/
|
|
12153
12927
|
close() {
|
|
12154
12928
|
if (this.closed) return;
|
|
12929
|
+
if (this.apiTransactionDepth > 0) {
|
|
12930
|
+
throw new SqliteError("cannot close database inside transaction()", "misuse");
|
|
12931
|
+
}
|
|
12155
12932
|
if (this.transactions.inTransaction) this.transactions.rollback();
|
|
12156
12933
|
this.closed = true;
|
|
12157
12934
|
}
|
|
12935
|
+
/**
|
|
12936
|
+
* Rows changed by the most recent INSERT / UPDATE / DELETE (SQLite `changes()`).
|
|
12937
|
+
*
|
|
12938
|
+
* @throws {SqliteError} If the database is closed.
|
|
12939
|
+
*/
|
|
12158
12940
|
get changes() {
|
|
12159
12941
|
this.assertOpen();
|
|
12160
12942
|
return this.state.changes;
|
|
12161
12943
|
}
|
|
12944
|
+
/**
|
|
12945
|
+
* Rowid of the most recent INSERT (SQLite `last_insert_rowid()`).
|
|
12946
|
+
*
|
|
12947
|
+
* @throws {SqliteError} If the database is closed.
|
|
12948
|
+
*/
|
|
12162
12949
|
get lastInsertRowid() {
|
|
12163
12950
|
this.assertOpen();
|
|
12164
12951
|
return this.state.lastInsertRowid;
|
|
12165
12952
|
}
|
|
12953
|
+
/**
|
|
12954
|
+
* Throw if {@link close} has already been called.
|
|
12955
|
+
* @internal
|
|
12956
|
+
* @throws {SqliteError} If the database is closed.
|
|
12957
|
+
*/
|
|
12166
12958
|
assertOpen() {
|
|
12167
12959
|
if (this.closed) throw new SqliteError("Database is closed", "misuse");
|
|
12168
12960
|
}
|
|
12961
|
+
prepareSingle(sql) {
|
|
12962
|
+
const statements = parse(sql);
|
|
12963
|
+
if (statements.length === 0) {
|
|
12964
|
+
throw new SqliteError("empty statement", "misuse");
|
|
12965
|
+
}
|
|
12966
|
+
if (statements.length > 1) {
|
|
12967
|
+
throw new SqliteError("query()/prepare() accept a single statement only; use exec() for scripts", "misuse");
|
|
12968
|
+
}
|
|
12969
|
+
return Statement.create(this, sql, statements);
|
|
12970
|
+
}
|
|
12169
12971
|
};
|
|
12972
|
+
var disposeKey = Symbol.dispose;
|
|
12973
|
+
if (typeof disposeKey === "symbol") {
|
|
12974
|
+
Object.defineProperty(Database.prototype, disposeKey, {
|
|
12975
|
+
value: function() {
|
|
12976
|
+
this.close();
|
|
12977
|
+
},
|
|
12978
|
+
writable: true,
|
|
12979
|
+
configurable: true
|
|
12980
|
+
});
|
|
12981
|
+
}
|
|
12170
12982
|
export {
|
|
12171
|
-
DEFAULT_DATABASE_SEED,
|
|
12172
|
-
DEFAULT_NOW,
|
|
12173
12983
|
Database,
|
|
12174
|
-
Prng,
|
|
12175
|
-
SqlReal,
|
|
12176
12984
|
SqliteError,
|
|
12177
|
-
Statement
|
|
12178
|
-
affinityFromTypeName,
|
|
12179
|
-
applyAffinity,
|
|
12180
|
-
asSqlReal,
|
|
12181
|
-
canonicalizeNumber,
|
|
12182
|
-
cloneSqlValue,
|
|
12183
|
-
coerceToNumber,
|
|
12184
|
-
compareSql,
|
|
12185
|
-
decodeDatabaseState,
|
|
12186
|
-
deriveSeed,
|
|
12187
|
-
encodeDatabaseState,
|
|
12188
|
-
evalExpr,
|
|
12189
|
-
fixedClock,
|
|
12190
|
-
globMatch,
|
|
12191
|
-
isSqlReal,
|
|
12192
|
-
isTruthySql,
|
|
12193
|
-
likeMatch,
|
|
12194
|
-
parse,
|
|
12195
|
-
resolveClock,
|
|
12196
|
-
sqlValueEquals,
|
|
12197
|
-
storageClassOf,
|
|
12198
|
-
toInteger,
|
|
12199
|
-
tokenize,
|
|
12200
|
-
typeofSql,
|
|
12201
|
-
utf8Decode,
|
|
12202
|
-
utf8Encode
|
|
12985
|
+
Statement
|
|
12203
12986
|
};
|
|
12204
12987
|
//# sourceMappingURL=index.js.map
|