@crvouga/sqlite-mem 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +156 -0
- package/COMPATIBILITY-AUDIT.md +7 -9
- package/COMPATIBILITY.md +4 -3
- package/README.md +152 -14
- package/compat/coverage.json +797 -512
- package/compat/requirements.json +1 -1
- package/dist/api/database.d.ts +90 -12
- package/dist/api/statement.d.ts +74 -17
- package/dist/ast/nodes.d.ts +11 -2
- package/dist/constraints/check.d.ts +5 -6
- package/dist/errors/index.d.ts +25 -1
- 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 +9 -5
- 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 +33 -12
- package/dist/index.js +900 -120
- 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 +5 -4
- 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/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 +14 -7
- 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,7 +1,14 @@
|
|
|
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
|
+
/** Optional SQLite result-code name such as `SQLITE_CONSTRAINT`. */
|
|
4
6
|
sqliteCode;
|
|
7
|
+
/**
|
|
8
|
+
* @param message - Human-readable error text.
|
|
9
|
+
* @param category - Coarse class; defaults to `"other"`.
|
|
10
|
+
* @param sqliteCode - Optional SQLite result-code name.
|
|
11
|
+
*/
|
|
5
12
|
constructor(message, category = "other", sqliteCode) {
|
|
6
13
|
super(message);
|
|
7
14
|
this.name = "SqliteError";
|
|
@@ -1176,10 +1183,14 @@ var Parser = class {
|
|
|
1176
1183
|
if (!this.match("ON")) return null;
|
|
1177
1184
|
this.expect("CONFLICT");
|
|
1178
1185
|
let targetColumns = null;
|
|
1186
|
+
let targetExprs = null;
|
|
1179
1187
|
if (this.match("LPAREN")) {
|
|
1180
1188
|
targetColumns = [];
|
|
1189
|
+
targetExprs = [];
|
|
1181
1190
|
do {
|
|
1182
|
-
|
|
1191
|
+
const expr = this.parseExprPrec(PREC.ADD);
|
|
1192
|
+
targetExprs.push(expr);
|
|
1193
|
+
targetColumns.push(expr.type === "column" ? expr.name : "");
|
|
1183
1194
|
} while (this.match("COMMA"));
|
|
1184
1195
|
this.expect("RPAREN");
|
|
1185
1196
|
}
|
|
@@ -1187,14 +1198,14 @@ var Parser = class {
|
|
|
1187
1198
|
if (this.match("WHERE")) targetWhere = this.parseExpr();
|
|
1188
1199
|
this.expect("DO");
|
|
1189
1200
|
if (this.match("NOTHING")) {
|
|
1190
|
-
return { targetColumns, targetWhere, action: "nothing" };
|
|
1201
|
+
return { targetColumns, targetExprs, targetWhere, action: "nothing" };
|
|
1191
1202
|
}
|
|
1192
1203
|
this.expect("UPDATE");
|
|
1193
1204
|
this.expect("SET");
|
|
1194
1205
|
const set = this.parseSetItems();
|
|
1195
1206
|
let where = null;
|
|
1196
1207
|
if (this.match("WHERE")) where = this.parseExpr();
|
|
1197
|
-
return { targetColumns, targetWhere, action: { set, where } };
|
|
1208
|
+
return { targetColumns, targetExprs, targetWhere, action: { set, where } };
|
|
1198
1209
|
}
|
|
1199
1210
|
parseSetItems() {
|
|
1200
1211
|
const items = [];
|
|
@@ -1361,7 +1372,8 @@ var Parser = class {
|
|
|
1361
1372
|
columns: [],
|
|
1362
1373
|
constraints: [],
|
|
1363
1374
|
asSelect,
|
|
1364
|
-
withoutRowid: false
|
|
1375
|
+
withoutRowid: false,
|
|
1376
|
+
strict: false
|
|
1365
1377
|
};
|
|
1366
1378
|
}
|
|
1367
1379
|
this.expect("LPAREN");
|
|
@@ -1376,12 +1388,30 @@ var Parser = class {
|
|
|
1376
1388
|
} while (this.match("COMMA"));
|
|
1377
1389
|
this.expect("RPAREN");
|
|
1378
1390
|
let withoutRowid = false;
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
if (
|
|
1382
|
-
|
|
1391
|
+
let strict = false;
|
|
1392
|
+
while (!this.at("SEMI") && !this.at("EOF")) {
|
|
1393
|
+
if (this.match("WITHOUT")) {
|
|
1394
|
+
const word = this.parseIdent();
|
|
1395
|
+
if (word.toUpperCase() !== "ROWID") this.syntaxError("expected ROWID after WITHOUT");
|
|
1396
|
+
withoutRowid = true;
|
|
1397
|
+
} else {
|
|
1398
|
+
const word = this.parseIdent();
|
|
1399
|
+
if (word.toUpperCase() !== "STRICT") this.syntaxError(`unexpected table option ${word}`);
|
|
1400
|
+
strict = true;
|
|
1401
|
+
}
|
|
1402
|
+
this.match("COMMA");
|
|
1383
1403
|
}
|
|
1384
|
-
return {
|
|
1404
|
+
return {
|
|
1405
|
+
type: "create_table",
|
|
1406
|
+
ifNotExists,
|
|
1407
|
+
temp,
|
|
1408
|
+
name,
|
|
1409
|
+
columns,
|
|
1410
|
+
constraints,
|
|
1411
|
+
asSelect: null,
|
|
1412
|
+
withoutRowid,
|
|
1413
|
+
strict
|
|
1414
|
+
};
|
|
1385
1415
|
}
|
|
1386
1416
|
parseColumnDef() {
|
|
1387
1417
|
const name = this.parseIdent();
|
|
@@ -1507,7 +1537,8 @@ var Parser = class {
|
|
|
1507
1537
|
this.expect("RPAREN");
|
|
1508
1538
|
}
|
|
1509
1539
|
const { onDelete, onUpdate } = this.parseFkActions();
|
|
1510
|
-
|
|
1540
|
+
const { deferrable, initiallyDeferred } = this.parseDeferrable();
|
|
1541
|
+
return { type: "references", table, columns, onDelete, onUpdate, deferrable, initiallyDeferred };
|
|
1511
1542
|
}
|
|
1512
1543
|
parseTableConstraint() {
|
|
1513
1544
|
let name = null;
|
|
@@ -1554,20 +1585,40 @@ var Parser = class {
|
|
|
1554
1585
|
this.expect("RPAREN");
|
|
1555
1586
|
}
|
|
1556
1587
|
const { onDelete, onUpdate } = this.parseFkActions();
|
|
1557
|
-
|
|
1588
|
+
const { deferrable, initiallyDeferred } = this.parseDeferrable();
|
|
1589
|
+
return {
|
|
1590
|
+
type: "foreign_key",
|
|
1591
|
+
columns,
|
|
1592
|
+
refTable,
|
|
1593
|
+
refColumns,
|
|
1594
|
+
onDelete,
|
|
1595
|
+
onUpdate,
|
|
1596
|
+
name,
|
|
1597
|
+
deferrable,
|
|
1598
|
+
initiallyDeferred
|
|
1599
|
+
};
|
|
1558
1600
|
}
|
|
1559
1601
|
this.syntaxError("expected table constraint");
|
|
1560
1602
|
}
|
|
1561
1603
|
parseIndexedColumns() {
|
|
1562
1604
|
const cols = [];
|
|
1563
1605
|
do {
|
|
1564
|
-
const
|
|
1606
|
+
const expr = this.parseExprPrec(PREC.ADD);
|
|
1565
1607
|
let collate = null;
|
|
1608
|
+
let inner = expr;
|
|
1609
|
+
if (inner.type === "collate") {
|
|
1610
|
+
collate = inner.collation;
|
|
1611
|
+
inner = inner.expr;
|
|
1612
|
+
}
|
|
1566
1613
|
if (this.match("COLLATE")) collate = this.parseIdent();
|
|
1567
1614
|
let order = null;
|
|
1568
1615
|
if (this.match("ASC")) order = "ASC";
|
|
1569
1616
|
else if (this.match("DESC")) order = "DESC";
|
|
1570
|
-
|
|
1617
|
+
if (inner.type === "column" && inner.table === null) {
|
|
1618
|
+
cols.push({ name: inner.name, collate, order });
|
|
1619
|
+
} else {
|
|
1620
|
+
cols.push({ name: "", collate, order, expr: inner });
|
|
1621
|
+
}
|
|
1571
1622
|
} while (this.match("COMMA"));
|
|
1572
1623
|
return cols;
|
|
1573
1624
|
}
|
|
@@ -1610,6 +1661,23 @@ var Parser = class {
|
|
|
1610
1661
|
}
|
|
1611
1662
|
return { onDelete, onUpdate };
|
|
1612
1663
|
}
|
|
1664
|
+
parseDeferrable() {
|
|
1665
|
+
let deferrable = false;
|
|
1666
|
+
let initiallyDeferred = false;
|
|
1667
|
+
if (this.match("NOT")) {
|
|
1668
|
+
this.expect("DEFERRABLE");
|
|
1669
|
+
} else if (this.match("DEFERRABLE")) {
|
|
1670
|
+
deferrable = true;
|
|
1671
|
+
}
|
|
1672
|
+
if (this.match("INITIALLY")) {
|
|
1673
|
+
if (this.match("DEFERRED")) initiallyDeferred = true;
|
|
1674
|
+
else {
|
|
1675
|
+
this.expect("IMMEDIATE");
|
|
1676
|
+
initiallyDeferred = false;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
return { deferrable, initiallyDeferred: deferrable && initiallyDeferred };
|
|
1680
|
+
}
|
|
1613
1681
|
parseOrConflict() {
|
|
1614
1682
|
if (!this.match("OR")) return null;
|
|
1615
1683
|
return this.parseConflictAction();
|
|
@@ -2214,13 +2282,19 @@ var Parser = class {
|
|
|
2214
2282
|
start = this.parseFrameBound(true);
|
|
2215
2283
|
end = { kind: "current_row" };
|
|
2216
2284
|
}
|
|
2217
|
-
|
|
2218
|
-
if (this.
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2285
|
+
let exclude = null;
|
|
2286
|
+
if (this.match("EXCLUDE")) {
|
|
2287
|
+
if (this.match("NO")) {
|
|
2288
|
+
this.expect("OTHERS");
|
|
2289
|
+
exclude = "no_others";
|
|
2290
|
+
} else if (this.match("CURRENT")) {
|
|
2291
|
+
this.expect("ROW");
|
|
2292
|
+
exclude = "current_row";
|
|
2293
|
+
} else if (this.match("GROUP")) exclude = "group";
|
|
2294
|
+
else if (this.match("TIES")) exclude = "ties";
|
|
2295
|
+
else this.syntaxError("expected EXCLUDE CURRENT ROW, GROUP, TIES, or NO OTHERS");
|
|
2296
|
+
}
|
|
2297
|
+
return { type, start, end, exclude };
|
|
2224
2298
|
}
|
|
2225
2299
|
parseFrameBound(precedingSide) {
|
|
2226
2300
|
if (this.match("UNBOUNDED")) {
|
|
@@ -2268,6 +2342,9 @@ var DEFAULT_DATABASE_SEED = 1;
|
|
|
2268
2342
|
// src/runtime/prng.ts
|
|
2269
2343
|
var Prng = class _Prng {
|
|
2270
2344
|
state;
|
|
2345
|
+
/**
|
|
2346
|
+
* @param seed - Engine seed. `0` is replaced with a non-zero constant.
|
|
2347
|
+
*/
|
|
2271
2348
|
constructor(seed = 1) {
|
|
2272
2349
|
let s = typeof seed === "bigint" ? seed : BigInt(seed | 0);
|
|
2273
2350
|
if (s === 0n) s = 0x9e3779b97f4a7c15n;
|
|
@@ -2289,10 +2366,16 @@ var Prng = class _Prng {
|
|
|
2289
2366
|
if (signed === -(1n << 63n)) signed += 1n;
|
|
2290
2367
|
return signed;
|
|
2291
2368
|
}
|
|
2369
|
+
/** Next float in `[0, 1)` from 53 bits of {@link nextU64}. */
|
|
2292
2370
|
nextFloat() {
|
|
2293
2371
|
const bits = this.nextU64();
|
|
2294
2372
|
return Number(bits >> 11n) / Number(1n << 53n);
|
|
2295
2373
|
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Next integer in `[min, max]` (inclusive).
|
|
2376
|
+
*
|
|
2377
|
+
* @throws {RangeError} If `max < min`.
|
|
2378
|
+
*/
|
|
2296
2379
|
nextInt(min, max) {
|
|
2297
2380
|
if (max < min) throw new RangeError("max < min");
|
|
2298
2381
|
const span = max - min + 1;
|
|
@@ -2302,9 +2385,11 @@ var Prng = class _Prng {
|
|
|
2302
2385
|
getState() {
|
|
2303
2386
|
return this.state;
|
|
2304
2387
|
}
|
|
2388
|
+
/** Restore unsigned 64-bit engine state from {@link getState}. */
|
|
2305
2389
|
setState(state) {
|
|
2306
2390
|
this.state = BigInt.asUintN(64, state);
|
|
2307
2391
|
}
|
|
2392
|
+
/** Independent copy with the same engine state. */
|
|
2308
2393
|
clone() {
|
|
2309
2394
|
const copy = new _Prng(1);
|
|
2310
2395
|
copy.state = this.state;
|
|
@@ -2325,6 +2410,7 @@ function deriveSeed(...parts) {
|
|
|
2325
2410
|
|
|
2326
2411
|
// src/types/value.ts
|
|
2327
2412
|
var SqlReal = class {
|
|
2413
|
+
/** IEEE float payload (`-0` already canonicalized to `+0`). */
|
|
2328
2414
|
value;
|
|
2329
2415
|
constructor(value) {
|
|
2330
2416
|
this.value = canonicalizeNumber(value);
|
|
@@ -3458,6 +3544,23 @@ function jsonArrowPath(right) {
|
|
|
3458
3544
|
function extractOne(root, path) {
|
|
3459
3545
|
return pathGet(root, parseJsonPath(path));
|
|
3460
3546
|
}
|
|
3547
|
+
function arrayInsertJson(root, path, value) {
|
|
3548
|
+
const steps = parseJsonPath(path);
|
|
3549
|
+
const last = steps[steps.length - 1];
|
|
3550
|
+
if (!last || last.kind === "root" || last.kind === "key") {
|
|
3551
|
+
throw new SqliteError("JSON path error", "other");
|
|
3552
|
+
}
|
|
3553
|
+
const result = cloneJson(root);
|
|
3554
|
+
const parentInfo = pathParent(result, steps);
|
|
3555
|
+
if (parentInfo?.parent.kind !== "array") return result;
|
|
3556
|
+
const parent = parentInfo.parent;
|
|
3557
|
+
let idx = parentInfo.index;
|
|
3558
|
+
if (last.kind === "fromEnd") idx = parent.elements.length - last.n;
|
|
3559
|
+
if (last.kind === "append") idx = parent.elements.length;
|
|
3560
|
+
if (idx < 0 || idx > parent.elements.length) return result;
|
|
3561
|
+
parent.elements.splice(idx, 0, cloneJson(value));
|
|
3562
|
+
return result;
|
|
3563
|
+
}
|
|
3461
3564
|
function mutateJson(root, path, value, mode) {
|
|
3462
3565
|
const steps = parseJsonPath(path);
|
|
3463
3566
|
if (steps.length <= 1) {
|
|
@@ -3678,6 +3781,26 @@ function textOf(value) {
|
|
|
3678
3781
|
if (isSqlJsonText(value)) return value.value;
|
|
3679
3782
|
return String(value);
|
|
3680
3783
|
}
|
|
3784
|
+
function arrayInsertArgs(args, asJsonb) {
|
|
3785
|
+
if (args.length === 0) {
|
|
3786
|
+
throw new SqliteError("wrong number of arguments to function json_array_insert()", "misuse");
|
|
3787
|
+
}
|
|
3788
|
+
if (args.length % 2 === 0) {
|
|
3789
|
+
throw new SqliteError("json_array_insert() requires an odd number of arguments", "misuse");
|
|
3790
|
+
}
|
|
3791
|
+
if (args.some((arg) => arg === null)) return null;
|
|
3792
|
+
try {
|
|
3793
|
+
let node = ensureJson(args[0]);
|
|
3794
|
+
for (let i = 1; i < args.length; i += 2) {
|
|
3795
|
+
const path = textOf(args[i]);
|
|
3796
|
+
const value = sqlValueToJsonNode(args[i + 1]);
|
|
3797
|
+
node = arrayInsertJson(node, path, value);
|
|
3798
|
+
}
|
|
3799
|
+
return asJsonb ? jsonNodeToSql(node, "jsonb") : jsonNodeToSql(node, "json");
|
|
3800
|
+
} catch (e) {
|
|
3801
|
+
wrapJsonError(e);
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3681
3804
|
function mutateArgs(args, mode, asJsonb) {
|
|
3682
3805
|
if (args.length === 0) throw new SqliteError(`wrong number of arguments to function json_${mode}()`, "misuse");
|
|
3683
3806
|
if (args.length % 2 === 0) {
|
|
@@ -3816,6 +3939,12 @@ var jsonScalarFunctions = {
|
|
|
3816
3939
|
jsonb_insert(args) {
|
|
3817
3940
|
return mutateArgs(args, "insert", true);
|
|
3818
3941
|
},
|
|
3942
|
+
json_array_insert(args) {
|
|
3943
|
+
return arrayInsertArgs(args, false);
|
|
3944
|
+
},
|
|
3945
|
+
jsonb_array_insert(args) {
|
|
3946
|
+
return arrayInsertArgs(args, true);
|
|
3947
|
+
},
|
|
3819
3948
|
json_replace(args) {
|
|
3820
3949
|
return mutateArgs(args, "replace", false);
|
|
3821
3950
|
},
|
|
@@ -4859,6 +4988,10 @@ var scalarFunctions = {
|
|
|
4859
4988
|
requireArgs3("sqlite_log", args, 2);
|
|
4860
4989
|
return null;
|
|
4861
4990
|
},
|
|
4991
|
+
load_extension(args) {
|
|
4992
|
+
requireArgs3("load_extension", args, 1, 2);
|
|
4993
|
+
throw new SqliteError("not authorized", "misuse");
|
|
4994
|
+
},
|
|
4862
4995
|
like(args) {
|
|
4863
4996
|
requireArgs3("like", args, 2, 3);
|
|
4864
4997
|
if (args[0] === null || args[1] === null || args[2] === null) return null;
|
|
@@ -5599,12 +5732,20 @@ function raiseActionName(expr) {
|
|
|
5599
5732
|
var IndexStore = class _IndexStore {
|
|
5600
5733
|
name;
|
|
5601
5734
|
entries;
|
|
5602
|
-
|
|
5735
|
+
keyValues;
|
|
5736
|
+
sortedKeys;
|
|
5737
|
+
frozen = false;
|
|
5738
|
+
constructor(name = "index", entries, keyValues) {
|
|
5603
5739
|
this.name = name;
|
|
5604
5740
|
this.entries = /* @__PURE__ */ new Map();
|
|
5741
|
+
this.keyValues = /* @__PURE__ */ new Map();
|
|
5742
|
+
this.sortedKeys = null;
|
|
5605
5743
|
if (entries) {
|
|
5606
5744
|
for (const [key, rowids] of entries) this.entries.set(key, [...rowids]);
|
|
5607
5745
|
}
|
|
5746
|
+
if (keyValues) {
|
|
5747
|
+
for (const [key, values] of keyValues) this.keyValues.set(key, [...values]);
|
|
5748
|
+
}
|
|
5608
5749
|
}
|
|
5609
5750
|
checkUnique(values, rowid) {
|
|
5610
5751
|
const key = serializeIndexKey(values);
|
|
@@ -5622,12 +5763,15 @@ var IndexStore = class _IndexStore {
|
|
|
5622
5763
|
}
|
|
5623
5764
|
}
|
|
5624
5765
|
insert(values, rowid, unique = true) {
|
|
5766
|
+
this.assertMutable();
|
|
5625
5767
|
const key = serializeIndexKey(values);
|
|
5626
5768
|
if (key === null) return;
|
|
5627
5769
|
if (unique) this.checkUnique(values, rowid);
|
|
5628
5770
|
const existing = this.entries.get(key);
|
|
5629
5771
|
if (!existing) {
|
|
5630
5772
|
this.entries.set(key, [rowid]);
|
|
5773
|
+
this.keyValues.set(key, [...values]);
|
|
5774
|
+
this.sortedKeys = null;
|
|
5631
5775
|
return;
|
|
5632
5776
|
}
|
|
5633
5777
|
for (const id of existing) if (sameRowid(id, rowid)) return;
|
|
@@ -5638,30 +5782,112 @@ var IndexStore = class _IndexStore {
|
|
|
5638
5782
|
if (key === null) return [];
|
|
5639
5783
|
return this.entries.get(key) ?? [];
|
|
5640
5784
|
}
|
|
5785
|
+
/** Leftmost prefix match: INDEX(a,b) used for WHERE a = ?. */
|
|
5786
|
+
lookupPrefix(values) {
|
|
5787
|
+
if (values.length === 0) return [];
|
|
5788
|
+
const prefix = serializeIndexKey(values);
|
|
5789
|
+
if (prefix === null) return [];
|
|
5790
|
+
const exact = this.entries.get(prefix);
|
|
5791
|
+
const needle = `${prefix}|`;
|
|
5792
|
+
const rowids = exact ? [...exact] : [];
|
|
5793
|
+
for (const [key, ids] of this.entries) {
|
|
5794
|
+
if (key.startsWith(needle)) rowids.push(...ids);
|
|
5795
|
+
}
|
|
5796
|
+
return rowids;
|
|
5797
|
+
}
|
|
5798
|
+
/**
|
|
5799
|
+
* Ordered scan of rowids for a single-column comparison.
|
|
5800
|
+
* `op` applies to the first key component.
|
|
5801
|
+
*/
|
|
5802
|
+
rangeLookup(op, bound, bound2) {
|
|
5803
|
+
const rowids = [];
|
|
5804
|
+
const keys = this.orderedKeys();
|
|
5805
|
+
for (const key of keys) {
|
|
5806
|
+
const values = this.keyValues.get(key);
|
|
5807
|
+
if (!values || values[0] === void 0) continue;
|
|
5808
|
+
const cmp = compareSerializedOrder(values[0], bound);
|
|
5809
|
+
let ok = false;
|
|
5810
|
+
if (op === ">") ok = cmp > 0;
|
|
5811
|
+
else if (op === ">=") ok = cmp >= 0;
|
|
5812
|
+
else if (op === "<") ok = cmp < 0;
|
|
5813
|
+
else if (op === "<=") ok = cmp <= 0;
|
|
5814
|
+
else if (op === "between" && bound2 !== void 0) {
|
|
5815
|
+
ok = cmp >= 0 && compareSerializedOrder(values[0], bound2) <= 0;
|
|
5816
|
+
}
|
|
5817
|
+
if (ok) {
|
|
5818
|
+
const ids = this.entries.get(key);
|
|
5819
|
+
if (ids) rowids.push(...ids);
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
return rowids;
|
|
5823
|
+
}
|
|
5824
|
+
orderedRowids(desc = false) {
|
|
5825
|
+
const keys = this.orderedKeys();
|
|
5826
|
+
const rowids = [];
|
|
5827
|
+
const seq = desc ? [...keys].reverse() : keys;
|
|
5828
|
+
for (const key of seq) {
|
|
5829
|
+
const ids = this.entries.get(key);
|
|
5830
|
+
if (ids) rowids.push(...ids);
|
|
5831
|
+
}
|
|
5832
|
+
return rowids;
|
|
5833
|
+
}
|
|
5641
5834
|
remove(values, rowid) {
|
|
5835
|
+
this.assertMutable();
|
|
5642
5836
|
const key = serializeIndexKey(values);
|
|
5643
5837
|
if (key === null) return false;
|
|
5644
5838
|
const existing = this.entries.get(key);
|
|
5645
5839
|
if (!existing) return false;
|
|
5646
5840
|
if (rowid === void 0) {
|
|
5647
5841
|
this.entries.delete(key);
|
|
5842
|
+
this.keyValues.delete(key);
|
|
5843
|
+
this.sortedKeys = null;
|
|
5648
5844
|
return true;
|
|
5649
5845
|
}
|
|
5650
5846
|
const index = existing.findIndex((id) => sameRowid(id, rowid));
|
|
5651
5847
|
if (index < 0) return false;
|
|
5652
5848
|
existing.splice(index, 1);
|
|
5653
|
-
if (existing.length === 0)
|
|
5849
|
+
if (existing.length === 0) {
|
|
5850
|
+
this.entries.delete(key);
|
|
5851
|
+
this.keyValues.delete(key);
|
|
5852
|
+
this.sortedKeys = null;
|
|
5853
|
+
}
|
|
5654
5854
|
return true;
|
|
5655
5855
|
}
|
|
5656
5856
|
clear() {
|
|
5857
|
+
this.assertMutable();
|
|
5657
5858
|
this.entries.clear();
|
|
5859
|
+
this.keyValues.clear();
|
|
5860
|
+
this.sortedKeys = null;
|
|
5658
5861
|
}
|
|
5659
5862
|
clone() {
|
|
5660
|
-
return new _IndexStore(this.name, this.entries);
|
|
5863
|
+
return new _IndexStore(this.name, this.entries, this.keyValues);
|
|
5864
|
+
}
|
|
5865
|
+
freeze() {
|
|
5866
|
+
this.frozen = true;
|
|
5661
5867
|
}
|
|
5662
5868
|
get size() {
|
|
5663
5869
|
return this.entries.size;
|
|
5664
5870
|
}
|
|
5871
|
+
orderedKeys() {
|
|
5872
|
+
if (this.sortedKeys) return this.sortedKeys;
|
|
5873
|
+
const keys = [...this.entries.keys()];
|
|
5874
|
+
keys.sort((a, b) => {
|
|
5875
|
+
const left = this.keyValues.get(a);
|
|
5876
|
+
const right = this.keyValues.get(b);
|
|
5877
|
+
if (!left || !right) return a < b ? -1 : a > b ? 1 : 0;
|
|
5878
|
+
const n = Math.min(left.length, right.length);
|
|
5879
|
+
for (let i = 0; i < n; i++) {
|
|
5880
|
+
const cmp = compareSql(left[i] ?? null, right[i] ?? null);
|
|
5881
|
+
if (cmp !== 0) return cmp ?? 0;
|
|
5882
|
+
}
|
|
5883
|
+
return left.length - right.length;
|
|
5884
|
+
});
|
|
5885
|
+
this.sortedKeys = keys;
|
|
5886
|
+
return keys;
|
|
5887
|
+
}
|
|
5888
|
+
assertMutable() {
|
|
5889
|
+
if (this.frozen) throw new SqliteError("internal: cannot mutate a frozen index", "other");
|
|
5890
|
+
}
|
|
5665
5891
|
};
|
|
5666
5892
|
function serializeIndexKey(values) {
|
|
5667
5893
|
if (values.some((value) => value === null)) return null;
|
|
@@ -5689,10 +5915,70 @@ function serializeValue(value) {
|
|
|
5689
5915
|
for (const byte of value) hex += byte.toString(16).padStart(2, "0");
|
|
5690
5916
|
return `b:${hex}`;
|
|
5691
5917
|
}
|
|
5918
|
+
function compareSerializedOrder(left, right) {
|
|
5919
|
+
return compareSql(left, right) ?? 0;
|
|
5920
|
+
}
|
|
5692
5921
|
function sameRowid(left, right) {
|
|
5693
5922
|
return typeof left === "bigint" || typeof right === "bigint" ? BigInt(left) === BigInt(right) : left === right;
|
|
5694
5923
|
}
|
|
5695
5924
|
|
|
5925
|
+
// src/types/strict.ts
|
|
5926
|
+
var STRICT_TYPES = /* @__PURE__ */ new Set(["INT", "INTEGER", "REAL", "TEXT", "BLOB", "ANY"]);
|
|
5927
|
+
function isStrictTypeName(typeName) {
|
|
5928
|
+
if (!typeName) return false;
|
|
5929
|
+
return STRICT_TYPES.has(typeName.trim().toUpperCase());
|
|
5930
|
+
}
|
|
5931
|
+
function applyStrictValue(value, typeName, table, column) {
|
|
5932
|
+
if (value === null) return null;
|
|
5933
|
+
const declared = typeName.trim().toUpperCase();
|
|
5934
|
+
const storage = storageClassOf(value);
|
|
5935
|
+
if (declared === "ANY") return value;
|
|
5936
|
+
if (declared === "TEXT") {
|
|
5937
|
+
if (storage === "text") return typeof value === "string" ? value : String(value.value);
|
|
5938
|
+
if (storage === "integer" || storage === "real") return String(isSqlReal(value) ? value.value : value);
|
|
5939
|
+
throw cannotStore(storage, declared, table, column);
|
|
5940
|
+
}
|
|
5941
|
+
if (declared === "INT" || declared === "INTEGER") {
|
|
5942
|
+
if (storage === "integer") return value;
|
|
5943
|
+
if (storage === "real") {
|
|
5944
|
+
const n = isSqlReal(value) ? value.value : Number(value);
|
|
5945
|
+
if (Number.isInteger(n)) return n;
|
|
5946
|
+
throw cannotStore(storage, declared, table, column);
|
|
5947
|
+
}
|
|
5948
|
+
if (storage === "text") {
|
|
5949
|
+
const n = coerceToNumber(value);
|
|
5950
|
+
if (n !== null && Number.isInteger(n) && String(value).trim() === String(n)) return n;
|
|
5951
|
+
throw cannotStore(storage, declared, table, column);
|
|
5952
|
+
}
|
|
5953
|
+
throw cannotStore(storage, declared, table, column);
|
|
5954
|
+
}
|
|
5955
|
+
if (declared === "REAL") {
|
|
5956
|
+
if (storage === "real") return isSqlReal(value) ? value : asSqlReal(Number(value));
|
|
5957
|
+
if (storage === "integer") return asSqlReal(typeof value === "bigint" ? Number(value) : Number(value));
|
|
5958
|
+
if (storage === "text") {
|
|
5959
|
+
const n = coerceToNumber(value);
|
|
5960
|
+
if (n !== null) return asSqlReal(n);
|
|
5961
|
+
}
|
|
5962
|
+
throw cannotStore(storage, declared, table, column);
|
|
5963
|
+
}
|
|
5964
|
+
if (declared === "BLOB") {
|
|
5965
|
+
if (storage === "blob") return value;
|
|
5966
|
+
throw cannotStore(storage, declared, table, column);
|
|
5967
|
+
}
|
|
5968
|
+
throw new SqliteError(`unknown datatype for ${table}.${column}: "${typeName}"`, "other");
|
|
5969
|
+
}
|
|
5970
|
+
function cannotStore(storage, declared, table, column) {
|
|
5971
|
+
const shown = storage === "integer" ? "INT" : storage.toUpperCase();
|
|
5972
|
+
throw new SqliteError(
|
|
5973
|
+
`cannot store ${shown} value in ${declared} column ${table}.${column}`,
|
|
5974
|
+
"datatype_mismatch",
|
|
5975
|
+
"SQLITE_CONSTRAINT_DATATYPE"
|
|
5976
|
+
);
|
|
5977
|
+
}
|
|
5978
|
+
function unknownStrictType(table, column, typeName) {
|
|
5979
|
+
throw new SqliteError(`unknown datatype for ${table}.${column}: "${typeName}"`, "other");
|
|
5980
|
+
}
|
|
5981
|
+
|
|
5696
5982
|
// src/vtable/fts/options.ts
|
|
5697
5983
|
var OPTION_NAMES = /* @__PURE__ */ new Set([
|
|
5698
5984
|
"tokenize",
|
|
@@ -7235,11 +7521,13 @@ var Table = class _Table {
|
|
|
7235
7521
|
indexes;
|
|
7236
7522
|
originalSql;
|
|
7237
7523
|
withoutRowid;
|
|
7524
|
+
strict;
|
|
7238
7525
|
/** Clustered PK key string → row for WITHOUT ROWID tables. */
|
|
7239
7526
|
clusteredRows;
|
|
7240
7527
|
scanCache = null;
|
|
7241
7528
|
/** Lazy covering hashes: column nameLower → serializeIndexKey → rowids. */
|
|
7242
7529
|
equalityHashes = null;
|
|
7530
|
+
frozen = false;
|
|
7243
7531
|
constructor(name, columns, options = {}) {
|
|
7244
7532
|
this.name = name;
|
|
7245
7533
|
this.columns = columns.map(cloneColumn);
|
|
@@ -7249,6 +7537,7 @@ var Table = class _Table {
|
|
|
7249
7537
|
this.indexes = [...options.indexes ?? []];
|
|
7250
7538
|
this.originalSql = options.originalSql ?? null;
|
|
7251
7539
|
this.withoutRowid = options.withoutRowid ?? false;
|
|
7540
|
+
this.strict = options.strict ?? false;
|
|
7252
7541
|
this.clusteredRows = /* @__PURE__ */ new Map();
|
|
7253
7542
|
this.scanCache = null;
|
|
7254
7543
|
}
|
|
@@ -7284,7 +7573,7 @@ var Table = class _Table {
|
|
|
7284
7573
|
}
|
|
7285
7574
|
/** True when the table has no user constraints that DML must enforce beyond hidden rowid. */
|
|
7286
7575
|
isUnconstrained() {
|
|
7287
|
-
if (this.withoutRowid || this.indexes.length > 0 || this.constraints.length > 0) return false;
|
|
7576
|
+
if (this.withoutRowid || this.strict || this.indexes.length > 0 || this.constraints.length > 0) return false;
|
|
7288
7577
|
for (const column of this.columns) {
|
|
7289
7578
|
if (column.notNull || column.primaryKey || column.unique || column.generated || column.defaultExpr) return false;
|
|
7290
7579
|
}
|
|
@@ -7311,7 +7600,7 @@ var Table = class _Table {
|
|
|
7311
7600
|
return rows;
|
|
7312
7601
|
}
|
|
7313
7602
|
ensureEqualityHash(columnLower) {
|
|
7314
|
-
if (this.rows.size < EQUALITY_HASH_MIN_ROWS) return false;
|
|
7603
|
+
if (this.frozen || this.rows.size < EQUALITY_HASH_MIN_ROWS) return false;
|
|
7315
7604
|
const column = this.columns.find((item) => item.nameLower === columnLower);
|
|
7316
7605
|
if (!column) return false;
|
|
7317
7606
|
this.equalityHashes ??= /* @__PURE__ */ new Map();
|
|
@@ -7361,6 +7650,7 @@ var Table = class _Table {
|
|
|
7361
7650
|
}
|
|
7362
7651
|
}
|
|
7363
7652
|
insert(input, options) {
|
|
7653
|
+
this.assertMutable();
|
|
7364
7654
|
const supplied = isInsertRow(input) ? input : { values: input };
|
|
7365
7655
|
const values = options?.prepared ? supplied.values instanceof Map ? supplied.values : rowValues(supplied.values) : this.prepareValues(supplied.values);
|
|
7366
7656
|
if (this.withoutRowid) {
|
|
@@ -7403,6 +7693,7 @@ var Table = class _Table {
|
|
|
7403
7693
|
return rowid;
|
|
7404
7694
|
}
|
|
7405
7695
|
update(rowid, updates) {
|
|
7696
|
+
this.assertMutable();
|
|
7406
7697
|
const key = canonicalRowid(rowid);
|
|
7407
7698
|
const existing = this.rows.get(key);
|
|
7408
7699
|
if (!existing) return void 0;
|
|
@@ -7445,6 +7736,7 @@ var Table = class _Table {
|
|
|
7445
7736
|
return candidate;
|
|
7446
7737
|
}
|
|
7447
7738
|
delete(rowid) {
|
|
7739
|
+
this.assertMutable();
|
|
7448
7740
|
const key = canonicalRowid(rowid);
|
|
7449
7741
|
const existing = this.rows.get(key);
|
|
7450
7742
|
if (!existing) return false;
|
|
@@ -7468,13 +7760,20 @@ var Table = class _Table {
|
|
|
7468
7760
|
constraints: this.constraints,
|
|
7469
7761
|
indexes: this.indexes,
|
|
7470
7762
|
originalSql: this.originalSql,
|
|
7471
|
-
withoutRowid: this.withoutRowid
|
|
7763
|
+
withoutRowid: this.withoutRowid,
|
|
7764
|
+
strict: this.strict
|
|
7472
7765
|
});
|
|
7473
7766
|
copy.nextRowid = this.nextRowid;
|
|
7474
7767
|
for (const [rowid, row] of this.rows) copy.rows.set(rowid, cloneRow(row));
|
|
7475
7768
|
for (const [clusterKey, row] of this.clusteredRows) copy.clusteredRows.set(clusterKey, cloneRow(row));
|
|
7476
7769
|
return copy;
|
|
7477
7770
|
}
|
|
7771
|
+
freeze() {
|
|
7772
|
+
this.frozen = true;
|
|
7773
|
+
}
|
|
7774
|
+
assertMutable() {
|
|
7775
|
+
if (this.frozen) throw new SqliteError("internal: cannot mutate a frozen table", "other");
|
|
7776
|
+
}
|
|
7478
7777
|
/** Rebuild clustered storage after snapshot decode or bulk load. */
|
|
7479
7778
|
rebuildClusteredRows() {
|
|
7480
7779
|
if (!this.withoutRowid) return;
|
|
@@ -7491,7 +7790,10 @@ var Table = class _Table {
|
|
|
7491
7790
|
const result = /* @__PURE__ */ new Map();
|
|
7492
7791
|
for (const column of this.columns) {
|
|
7493
7792
|
const key = normalizeColumnName(column.name);
|
|
7494
|
-
result.set(
|
|
7793
|
+
result.set(
|
|
7794
|
+
key,
|
|
7795
|
+
this.strict ? applyStrictValue(cloneSqlValue(supplied.get(key) ?? null), column.typeName ?? "", this.name, column.name) : applyAffinity(cloneSqlValue(supplied.get(key) ?? null), column.affinity)
|
|
7796
|
+
);
|
|
7495
7797
|
}
|
|
7496
7798
|
return result;
|
|
7497
7799
|
}
|
|
@@ -7833,6 +8135,12 @@ var DatabaseState = class _DatabaseState {
|
|
|
7833
8135
|
if (stmt.columns.length === 0) {
|
|
7834
8136
|
throw new SqliteError("table must have at least one column", "syntax");
|
|
7835
8137
|
}
|
|
8138
|
+
if (stmt.strict) {
|
|
8139
|
+
for (const definition of stmt.columns) {
|
|
8140
|
+
const typeName = definition.typeName?.trim() ?? "";
|
|
8141
|
+
if (!isStrictTypeName(typeName)) unknownStrictType(bare, definition.name, typeName || "null");
|
|
8142
|
+
}
|
|
8143
|
+
}
|
|
7836
8144
|
const normalizedConstraints = [...stmt.constraints];
|
|
7837
8145
|
for (const definition of stmt.columns) {
|
|
7838
8146
|
for (const constraint of definition.constraints) {
|
|
@@ -7846,7 +8154,9 @@ var DatabaseState = class _DatabaseState {
|
|
|
7846
8154
|
refColumns: constraint.columns,
|
|
7847
8155
|
onDelete: constraint.onDelete,
|
|
7848
8156
|
onUpdate: constraint.onUpdate,
|
|
7849
|
-
name: null
|
|
8157
|
+
name: null,
|
|
8158
|
+
deferrable: constraint.deferrable,
|
|
8159
|
+
initiallyDeferred: constraint.initiallyDeferred
|
|
7850
8160
|
});
|
|
7851
8161
|
}
|
|
7852
8162
|
}
|
|
@@ -7854,7 +8164,8 @@ var DatabaseState = class _DatabaseState {
|
|
|
7854
8164
|
const table = new Table(bare, columns, {
|
|
7855
8165
|
constraints: normalizedConstraints,
|
|
7856
8166
|
originalSql,
|
|
7857
|
-
withoutRowid: stmt.withoutRowid
|
|
8167
|
+
withoutRowid: stmt.withoutRowid,
|
|
8168
|
+
strict: stmt.strict
|
|
7858
8169
|
});
|
|
7859
8170
|
if (stmt.withoutRowid) {
|
|
7860
8171
|
const pk = columns.filter((column) => column.primaryKey);
|
|
@@ -7937,7 +8248,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
7937
8248
|
return this.databaseForSchema(oldSchema, oldName).renameTable(oldBare, splitQualifiedName(newName).bare);
|
|
7938
8249
|
}
|
|
7939
8250
|
const oldKey = keyOf(oldBare);
|
|
7940
|
-
const table = this.
|
|
8251
|
+
const table = this.getWritableTable(oldName);
|
|
7941
8252
|
const { schema: newSchema, bare: newBare } = splitQualifiedName(newName);
|
|
7942
8253
|
if (newSchema !== null) {
|
|
7943
8254
|
throw new SqliteError(`no such table: ${newName}`, "no_such_table");
|
|
@@ -7946,11 +8257,13 @@ var DatabaseState = class _DatabaseState {
|
|
|
7946
8257
|
this.tables.delete(oldKey);
|
|
7947
8258
|
table.name = newBare;
|
|
7948
8259
|
this.tables.set(keyOf(newBare), table);
|
|
7949
|
-
for (const index of this.indexes.values()) {
|
|
7950
|
-
if (keyOf(index.tableName)
|
|
8260
|
+
for (const index of [...this.indexes.values()]) {
|
|
8261
|
+
if (keyOf(index.tableName) !== oldKey) continue;
|
|
8262
|
+
this.ensureWritableIndex(index).tableName = newBare;
|
|
7951
8263
|
}
|
|
7952
|
-
for (const trigger of this.triggers
|
|
7953
|
-
if (keyOf(trigger.tableName)
|
|
8264
|
+
for (const [key, trigger] of this.triggers) {
|
|
8265
|
+
if (keyOf(trigger.tableName) !== oldKey) continue;
|
|
8266
|
+
this.triggers.set(key, { ...trigger, tableName: newBare });
|
|
7954
8267
|
}
|
|
7955
8268
|
this.schemaVersion++;
|
|
7956
8269
|
return table;
|
|
@@ -7985,7 +8298,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
7985
8298
|
return deleted;
|
|
7986
8299
|
}
|
|
7987
8300
|
createIndex(stmt, originalSql = null) {
|
|
7988
|
-
const table = this.
|
|
8301
|
+
const table = this.getWritableTable(stmt.table);
|
|
7989
8302
|
const { schema, bare } = splitQualifiedName(stmt.name);
|
|
7990
8303
|
const target = schema !== null ? this.databaseForSchema(schema, stmt.name) : this.databaseForTable(table);
|
|
7991
8304
|
const key = keyOf(bare);
|
|
@@ -7993,6 +8306,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
7993
8306
|
if (existing && stmt.ifNotExists) return existing;
|
|
7994
8307
|
target.assertSchemaNameAvailable(bare);
|
|
7995
8308
|
for (const indexed of stmt.columns) {
|
|
8309
|
+
if (indexed.expr) continue;
|
|
7996
8310
|
if (!table.columns.some((column) => keyOf(column.name) === keyOf(indexed.name))) {
|
|
7997
8311
|
throw new SqliteError(`no such column: ${indexed.name}`, "no_such_column");
|
|
7998
8312
|
}
|
|
@@ -8022,7 +8336,10 @@ var DatabaseState = class _DatabaseState {
|
|
|
8022
8336
|
}
|
|
8023
8337
|
target.indexes.delete(key);
|
|
8024
8338
|
const table = target.tables.get(keyOf(index.tableName));
|
|
8025
|
-
if (table)
|
|
8339
|
+
if (table) {
|
|
8340
|
+
const writable = target.ensureWritableTable(table);
|
|
8341
|
+
writable.indexes = writable.indexes.filter((item) => keyOf(item) !== key);
|
|
8342
|
+
}
|
|
8026
8343
|
target.schemaVersion++;
|
|
8027
8344
|
return true;
|
|
8028
8345
|
}
|
|
@@ -8106,6 +8423,71 @@ var DatabaseState = class _DatabaseState {
|
|
|
8106
8423
|
copy.totalChanges = this.totalChanges;
|
|
8107
8424
|
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8108
8425
|
copy.schemaVersion = this.schemaVersion;
|
|
8426
|
+
copy.userVersion = this.userVersion;
|
|
8427
|
+
return copy;
|
|
8428
|
+
}
|
|
8429
|
+
/** Share table/index objects with the live state. Callers must freeze first. */
|
|
8430
|
+
cloneShallow() {
|
|
8431
|
+
const copy = new _DatabaseState();
|
|
8432
|
+
copy.tables = new Map(this.tables);
|
|
8433
|
+
for (const [key, table] of this.virtualTables) copy.virtualTables.set(key, table.clone());
|
|
8434
|
+
copy.views = new Map(this.views);
|
|
8435
|
+
copy.indexes = new Map(this.indexes);
|
|
8436
|
+
copy.triggers = new Map(this.triggers);
|
|
8437
|
+
for (const [name, attached] of this.attached) {
|
|
8438
|
+
copy.attached.set(name, { state: attached.state.cloneShallow(), filename: attached.filename });
|
|
8439
|
+
}
|
|
8440
|
+
copy.lastInsertRowid = this.lastInsertRowid;
|
|
8441
|
+
copy.changes = this.changes;
|
|
8442
|
+
copy.totalChanges = this.totalChanges;
|
|
8443
|
+
copy.foreignKeysEnabled = this.foreignKeysEnabled;
|
|
8444
|
+
copy.schemaVersion = this.schemaVersion;
|
|
8445
|
+
copy.userVersion = this.userVersion;
|
|
8446
|
+
return copy;
|
|
8447
|
+
}
|
|
8448
|
+
freezeShared() {
|
|
8449
|
+
for (const table of this.tables.values()) table.freeze();
|
|
8450
|
+
for (const index of this.indexes.values()) index.store.freeze();
|
|
8451
|
+
for (const attached of this.attached.values()) attached.state.freezeShared();
|
|
8452
|
+
}
|
|
8453
|
+
thawShared() {
|
|
8454
|
+
for (const table of this.tables.values()) table.frozen = false;
|
|
8455
|
+
for (const index of this.indexes.values()) index.store.frozen = false;
|
|
8456
|
+
for (const attached of this.attached.values()) attached.state.thawShared();
|
|
8457
|
+
}
|
|
8458
|
+
getWritableTable(name) {
|
|
8459
|
+
const { schema, bare } = splitQualifiedName(name);
|
|
8460
|
+
if (schema !== null) {
|
|
8461
|
+
return this.databaseForSchema(schema, name).getWritableTable(bare);
|
|
8462
|
+
}
|
|
8463
|
+
const table = this.getTable(bare);
|
|
8464
|
+
return this.ensureWritableTable(table);
|
|
8465
|
+
}
|
|
8466
|
+
ensureWritableTable(table) {
|
|
8467
|
+
if (!table.frozen) return table;
|
|
8468
|
+
const key = keyOf(table.name);
|
|
8469
|
+
const copy = table.clone();
|
|
8470
|
+
this.tables.set(key, copy);
|
|
8471
|
+
return copy;
|
|
8472
|
+
}
|
|
8473
|
+
getWritableIndex(name) {
|
|
8474
|
+
const key = keyOf(name);
|
|
8475
|
+
const index = this.indexes.get(key);
|
|
8476
|
+
if (!index) throw new SqliteError(`no such index: ${name}`, "other");
|
|
8477
|
+
return this.ensureWritableIndex(index);
|
|
8478
|
+
}
|
|
8479
|
+
ensureWritableIndex(index) {
|
|
8480
|
+
if (!index.store.frozen) return index;
|
|
8481
|
+
const copy = {
|
|
8482
|
+
name: index.name,
|
|
8483
|
+
tableName: index.tableName,
|
|
8484
|
+
unique: index.unique,
|
|
8485
|
+
columns: structuredClone(index.columns),
|
|
8486
|
+
where: index.where ? structuredClone(index.where) : null,
|
|
8487
|
+
originalSql: index.originalSql,
|
|
8488
|
+
store: index.store.clone()
|
|
8489
|
+
};
|
|
8490
|
+
this.indexes.set(keyOf(index.name), copy);
|
|
8109
8491
|
return copy;
|
|
8110
8492
|
}
|
|
8111
8493
|
replaceWith(state, options) {
|
|
@@ -8121,6 +8503,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
8121
8503
|
this.totalChanges = copy.totalChanges;
|
|
8122
8504
|
this.foreignKeysEnabled = copy.foreignKeysEnabled;
|
|
8123
8505
|
this.schemaVersion = copy.schemaVersion;
|
|
8506
|
+
this.userVersion = copy.userVersion;
|
|
8124
8507
|
}
|
|
8125
8508
|
assertSchemaNameAvailable(name) {
|
|
8126
8509
|
const key = keyOf(name);
|
|
@@ -8312,7 +8695,8 @@ function encodeDatabaseState(state, runtime) {
|
|
|
8312
8695
|
constraints: table.constraints,
|
|
8313
8696
|
indexes: [...table.indexes].sort(compareNames),
|
|
8314
8697
|
originalSql: table.originalSql,
|
|
8315
|
-
withoutRowid: table.withoutRowid || void 0
|
|
8698
|
+
withoutRowid: table.withoutRowid || void 0,
|
|
8699
|
+
strict: table.strict || void 0
|
|
8316
8700
|
});
|
|
8317
8701
|
writer.value(table.nextRowid);
|
|
8318
8702
|
const rows = [...table.rows.values()].sort((a, b) => compareRowids2(a.rowid, b.rowid));
|
|
@@ -8364,7 +8748,8 @@ function decodeDatabaseState(snapshot) {
|
|
|
8364
8748
|
constraints: meta.constraints,
|
|
8365
8749
|
indexes: meta.indexes,
|
|
8366
8750
|
originalSql: meta.originalSql,
|
|
8367
|
-
withoutRowid: meta.withoutRowid ?? false
|
|
8751
|
+
withoutRowid: meta.withoutRowid ?? false,
|
|
8752
|
+
strict: meta.strict ?? false
|
|
8368
8753
|
});
|
|
8369
8754
|
table.nextRowid = asRowid2(reader.value());
|
|
8370
8755
|
const rowCount = reader.u32();
|
|
@@ -8478,7 +8863,8 @@ var TransactionManager = class {
|
|
|
8478
8863
|
if (this.inTransaction) {
|
|
8479
8864
|
throw new SqliteError("cannot start a transaction within a transaction", "transaction", "SQLITE_ERROR");
|
|
8480
8865
|
}
|
|
8481
|
-
this.
|
|
8866
|
+
this.state.freezeShared();
|
|
8867
|
+
this.transactionSnapshot = this.state.cloneShallow();
|
|
8482
8868
|
this.transactionPrngState = this.prng.getState();
|
|
8483
8869
|
this.savepoints = [];
|
|
8484
8870
|
this.startedBySavepoint = false;
|
|
@@ -8489,6 +8875,7 @@ var TransactionManager = class {
|
|
|
8489
8875
|
this.transactionPrngState = null;
|
|
8490
8876
|
this.savepoints = [];
|
|
8491
8877
|
this.startedBySavepoint = false;
|
|
8878
|
+
this.state.thawShared();
|
|
8492
8879
|
}
|
|
8493
8880
|
rollback(savepoint) {
|
|
8494
8881
|
this.requireTransaction("cannot rollback - no transaction is active");
|
|
@@ -8496,7 +8883,7 @@ var TransactionManager = class {
|
|
|
8496
8883
|
const index = this.findSavepoint(savepoint);
|
|
8497
8884
|
const snapshot2 = this.savepoints[index];
|
|
8498
8885
|
if (!snapshot2) throw new SqliteError(`no such savepoint: ${savepoint}`, "transaction", "SQLITE_ERROR");
|
|
8499
|
-
this.state.replaceWith(snapshot2.state);
|
|
8886
|
+
this.state.replaceWith(snapshot2.state, { adopt: true });
|
|
8500
8887
|
this.prng.setState(snapshot2.prngState);
|
|
8501
8888
|
this.savepoints.splice(index + 1);
|
|
8502
8889
|
return;
|
|
@@ -8506,22 +8893,26 @@ var TransactionManager = class {
|
|
|
8506
8893
|
if (!snapshot || prngState === null) {
|
|
8507
8894
|
throw new SqliteError("cannot rollback - no transaction is active", "transaction", "SQLITE_ERROR");
|
|
8508
8895
|
}
|
|
8509
|
-
this.state.replaceWith(snapshot);
|
|
8896
|
+
this.state.replaceWith(snapshot, { adopt: true });
|
|
8510
8897
|
this.prng.setState(prngState);
|
|
8511
8898
|
this.transactionSnapshot = null;
|
|
8512
8899
|
this.transactionPrngState = null;
|
|
8513
8900
|
this.savepoints = [];
|
|
8514
8901
|
this.startedBySavepoint = false;
|
|
8902
|
+
this.state.thawShared();
|
|
8515
8903
|
}
|
|
8516
8904
|
savepoint(name) {
|
|
8517
8905
|
if (!this.inTransaction) {
|
|
8518
|
-
this.
|
|
8906
|
+
this.state.freezeShared();
|
|
8907
|
+
this.transactionSnapshot = this.state.cloneShallow();
|
|
8519
8908
|
this.transactionPrngState = this.prng.getState();
|
|
8520
8909
|
this.startedBySavepoint = true;
|
|
8910
|
+
} else {
|
|
8911
|
+
this.state.freezeShared();
|
|
8521
8912
|
}
|
|
8522
8913
|
this.savepoints.push({
|
|
8523
8914
|
name,
|
|
8524
|
-
state: this.state.
|
|
8915
|
+
state: this.state.cloneShallow(),
|
|
8525
8916
|
prngState: this.prng.getState()
|
|
8526
8917
|
});
|
|
8527
8918
|
}
|
|
@@ -8533,6 +8924,7 @@ var TransactionManager = class {
|
|
|
8533
8924
|
this.transactionSnapshot = null;
|
|
8534
8925
|
this.transactionPrngState = null;
|
|
8535
8926
|
this.startedBySavepoint = false;
|
|
8927
|
+
this.state.thawShared();
|
|
8536
8928
|
}
|
|
8537
8929
|
}
|
|
8538
8930
|
findSavepoint(name) {
|
|
@@ -8721,6 +9113,7 @@ function toSqlValue(value) {
|
|
|
8721
9113
|
return canonicalizeNumber(value);
|
|
8722
9114
|
}
|
|
8723
9115
|
if (typeof value === "boolean") return value ? 1 : 0;
|
|
9116
|
+
if (value instanceof SqlReal || value instanceof SqlJsonText) return value;
|
|
8724
9117
|
if (value instanceof Uint8Array) return new Uint8Array(value);
|
|
8725
9118
|
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
8726
9119
|
throw new SqliteError(`unsupported bind value: ${typeof value}`, "datatype_mismatch");
|
|
@@ -8935,6 +9328,44 @@ function buildSqliteMaster(state) {
|
|
|
8935
9328
|
return buildSchemaCatalog(state, "sqlite_master");
|
|
8936
9329
|
}
|
|
8937
9330
|
|
|
9331
|
+
// src/expressions/equals.ts
|
|
9332
|
+
function exprEquals(left, right) {
|
|
9333
|
+
if (left.type !== right.type) return false;
|
|
9334
|
+
switch (left.type) {
|
|
9335
|
+
case "null":
|
|
9336
|
+
return true;
|
|
9337
|
+
case "literal":
|
|
9338
|
+
return right.type === "literal" && valuesEqual(left.value, right.value);
|
|
9339
|
+
case "column":
|
|
9340
|
+
return right.type === "column" && (left.table ?? "").toLowerCase() === (right.table ?? "").toLowerCase() && left.name.toLowerCase() === right.name.toLowerCase();
|
|
9341
|
+
case "unary":
|
|
9342
|
+
return right.type === "unary" && left.op === right.op && exprEquals(left.expr, right.expr);
|
|
9343
|
+
case "binary":
|
|
9344
|
+
return right.type === "binary" && left.op === right.op && exprEquals(left.left, right.left) && exprEquals(left.right, right.right);
|
|
9345
|
+
case "function":
|
|
9346
|
+
return right.type === "function" && left.name.toLowerCase() === right.name.toLowerCase() && left.distinct === right.distinct && argsEqual(left.args, right.args);
|
|
9347
|
+
case "collate":
|
|
9348
|
+
return right.type === "collate" && left.collation.toLowerCase() === right.collation.toLowerCase() && exprEquals(left.expr, right.expr);
|
|
9349
|
+
case "cast":
|
|
9350
|
+
return right.type === "cast" && (left.typeName ?? "").toUpperCase() === (right.typeName ?? "").toUpperCase() && exprEquals(left.expr, right.expr);
|
|
9351
|
+
default:
|
|
9352
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
9353
|
+
}
|
|
9354
|
+
}
|
|
9355
|
+
function argsEqual(left, right) {
|
|
9356
|
+
if (left === "*" || right === "*") return left === right;
|
|
9357
|
+
if (left.length !== right.length) return false;
|
|
9358
|
+
return left.every((expr, index) => exprEquals(expr, right[index]));
|
|
9359
|
+
}
|
|
9360
|
+
function valuesEqual(left, right) {
|
|
9361
|
+
if (left === right) return true;
|
|
9362
|
+
if (left instanceof Uint8Array && right instanceof Uint8Array) {
|
|
9363
|
+
if (left.length !== right.length) return false;
|
|
9364
|
+
return left.every((byte, index) => byte === right[index]);
|
|
9365
|
+
}
|
|
9366
|
+
return false;
|
|
9367
|
+
}
|
|
9368
|
+
|
|
8938
9369
|
// src/planner/access.ts
|
|
8939
9370
|
function conjunctions(expr) {
|
|
8940
9371
|
if (expr.type === "binary" && expr.op === "AND") {
|
|
@@ -8955,6 +9386,16 @@ function equalityAgainstConst(expr) {
|
|
|
8955
9386
|
}
|
|
8956
9387
|
return null;
|
|
8957
9388
|
}
|
|
9389
|
+
function equalityAgainstExpr(expr) {
|
|
9390
|
+
if (expr.type !== "binary" || expr.op !== "=" && expr.op !== "==") return null;
|
|
9391
|
+
if (expr.left.type !== "column" && isRowIndependentExpr(expr.right)) {
|
|
9392
|
+
return { expr: expr.left, valueExpr: expr.right };
|
|
9393
|
+
}
|
|
9394
|
+
if (expr.right.type !== "column" && isRowIndependentExpr(expr.left)) {
|
|
9395
|
+
return { expr: expr.right, valueExpr: expr.left };
|
|
9396
|
+
}
|
|
9397
|
+
return null;
|
|
9398
|
+
}
|
|
8958
9399
|
function whereFullyCovered(where, coveredColumns, alias, tableName) {
|
|
8959
9400
|
for (const part of conjunctions(where)) {
|
|
8960
9401
|
const eq = equalityAgainstConst(part);
|
|
@@ -8991,8 +9432,8 @@ function lookupTableRows(from, where, env) {
|
|
|
8991
9432
|
} catch {
|
|
8992
9433
|
return null;
|
|
8993
9434
|
}
|
|
8994
|
-
const
|
|
8995
|
-
|
|
9435
|
+
const parts = conjunctions(where);
|
|
9436
|
+
const equalities = parts.map(equalityAgainstConst).filter((item) => item !== null).filter((item) => matchesTable(item.table, alias, table.name));
|
|
8996
9437
|
const evalConst = (expr) => {
|
|
8997
9438
|
try {
|
|
8998
9439
|
return evalExpr(expr, env.createEvalContext(null));
|
|
@@ -9006,7 +9447,6 @@ function lookupTableRows(from, where, env) {
|
|
|
9006
9447
|
if (value === void 0) continue;
|
|
9007
9448
|
resolved.push({ table: eq.table, column: eq.column, value });
|
|
9008
9449
|
}
|
|
9009
|
-
if (resolved.length === 0) return null;
|
|
9010
9450
|
const pk = table.integerPkColumn();
|
|
9011
9451
|
for (const eq of resolved) {
|
|
9012
9452
|
const col = eq.column.toLowerCase();
|
|
@@ -9026,17 +9466,40 @@ function lookupTableRows(from, where, env) {
|
|
|
9026
9466
|
let best = null;
|
|
9027
9467
|
for (const indexName of table.indexes) {
|
|
9028
9468
|
const index = db.indexes.get(indexName.toLowerCase());
|
|
9029
|
-
if (!index
|
|
9469
|
+
if (!index) continue;
|
|
9470
|
+
if (index.where && !parts.some((part) => exprEquals(part, index.where))) continue;
|
|
9471
|
+
if (index.columns.some((column) => column.expr)) {
|
|
9472
|
+
const prefix2 = [];
|
|
9473
|
+
for (const column of index.columns) {
|
|
9474
|
+
if (!column.expr) {
|
|
9475
|
+
const value2 = byColumn.get(column.name.toLowerCase());
|
|
9476
|
+
if (value2 === void 0) break;
|
|
9477
|
+
prefix2.push(normalizeForCollation(value2, column.collate ?? "BINARY"));
|
|
9478
|
+
continue;
|
|
9479
|
+
}
|
|
9480
|
+
const match = parts.map(equalityAgainstExpr).find((item) => item && exprEquals(item.expr, column.expr));
|
|
9481
|
+
if (!match) break;
|
|
9482
|
+
const value = evalConst(match.valueExpr);
|
|
9483
|
+
if (value === void 0) break;
|
|
9484
|
+
prefix2.push(normalizeForCollation(value, column.collate ?? "BINARY"));
|
|
9485
|
+
}
|
|
9486
|
+
if (prefix2.length === 0) continue;
|
|
9487
|
+
const rowids2 = prefix2.length === index.columns.length ? index.store.lookup(prefix2) : index.store.lookupPrefix(prefix2);
|
|
9488
|
+
best = { rowids: rowids2, columns: index.columns.map((column) => column.name.toLowerCase()) };
|
|
9489
|
+
if (index.unique || rowids2.length <= 1) break;
|
|
9490
|
+
continue;
|
|
9491
|
+
}
|
|
9030
9492
|
const prefix = [];
|
|
9031
9493
|
for (const column of index.columns) {
|
|
9032
9494
|
const value = byColumn.get(column.name.toLowerCase());
|
|
9033
9495
|
if (value === void 0) break;
|
|
9034
9496
|
prefix.push(normalizeForCollation(value, column.collate ?? "BINARY"));
|
|
9035
9497
|
}
|
|
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
|
|
9498
|
+
if (prefix.length === 0) continue;
|
|
9499
|
+
const rowids = prefix.length === index.columns.length ? index.store.lookup(prefix) : index.store.lookupPrefix(prefix);
|
|
9500
|
+
best = { rowids, columns: index.columns.slice(0, prefix.length).map((column) => column.name.toLowerCase()) };
|
|
9501
|
+
if (index.unique && prefix.length === index.columns.length) break;
|
|
9502
|
+
if (rowids.length <= 1) break;
|
|
9040
9503
|
}
|
|
9041
9504
|
if (best) {
|
|
9042
9505
|
const rows = [];
|
|
@@ -9055,6 +9518,77 @@ function lookupTableRows(from, where, env) {
|
|
|
9055
9518
|
hashed.sort((a, b) => compareRowids3(a.rowid, b.rowid));
|
|
9056
9519
|
return { table, alias, rows: hashed, coveredColumns: /* @__PURE__ */ new Set([col]) };
|
|
9057
9520
|
}
|
|
9521
|
+
const range = rangeAgainstConst(where) ?? rangeFromBetween(where);
|
|
9522
|
+
if (range && matchesTable(range.table, alias, table.name)) {
|
|
9523
|
+
const value = evalConst(range.valueExpr);
|
|
9524
|
+
const value2 = range.valueExpr2 ? evalConst(range.valueExpr2) : void 0;
|
|
9525
|
+
if (value !== void 0) {
|
|
9526
|
+
for (const indexName of table.indexes) {
|
|
9527
|
+
const index = db.indexes.get(indexName.toLowerCase());
|
|
9528
|
+
if (!index || index.where || index.columns.length !== 1) continue;
|
|
9529
|
+
if (index.columns[0].expr) continue;
|
|
9530
|
+
if (index.columns[0].name.toLowerCase() !== range.column.toLowerCase()) continue;
|
|
9531
|
+
const bound = normalizeForCollation(value, index.columns[0].collate ?? "BINARY");
|
|
9532
|
+
const bound2 = value2 !== void 0 ? normalizeForCollation(value2, index.columns[0].collate ?? "BINARY") : void 0;
|
|
9533
|
+
const rowids = index.store.rangeLookup(range.op, bound, bound2);
|
|
9534
|
+
const rows = [];
|
|
9535
|
+
for (const rowid of rowids) {
|
|
9536
|
+
const row = table.get(rowid);
|
|
9537
|
+
if (row) rows.push(row);
|
|
9538
|
+
}
|
|
9539
|
+
return { table, alias, rows, coveredColumns: /* @__PURE__ */ new Set([range.column.toLowerCase()]) };
|
|
9540
|
+
}
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
return null;
|
|
9544
|
+
}
|
|
9545
|
+
function rangeAgainstConst(expr) {
|
|
9546
|
+
if (expr.type !== "binary") return null;
|
|
9547
|
+
const op = expr.op;
|
|
9548
|
+
if (op !== ">" && op !== ">=" && op !== "<" && op !== "<=") return null;
|
|
9549
|
+
if (expr.left.type === "column" && isRowIndependentExpr(expr.right)) {
|
|
9550
|
+
return { table: expr.left.table, column: expr.left.name, op, valueExpr: expr.right };
|
|
9551
|
+
}
|
|
9552
|
+
if (expr.right.type === "column" && isRowIndependentExpr(expr.left)) {
|
|
9553
|
+
const flipped = op === ">" ? "<" : op === ">=" ? "<=" : op === "<" ? ">" : ">=";
|
|
9554
|
+
return { table: expr.right.table, column: expr.right.name, op: flipped, valueExpr: expr.left };
|
|
9555
|
+
}
|
|
9556
|
+
return null;
|
|
9557
|
+
}
|
|
9558
|
+
function rangeFromBetween(expr) {
|
|
9559
|
+
if (expr.type !== "between" || expr.not || expr.expr.type !== "column") return null;
|
|
9560
|
+
return {
|
|
9561
|
+
table: expr.expr.table,
|
|
9562
|
+
column: expr.expr.name,
|
|
9563
|
+
op: "between",
|
|
9564
|
+
valueExpr: expr.lower,
|
|
9565
|
+
valueExpr2: expr.upper
|
|
9566
|
+
};
|
|
9567
|
+
}
|
|
9568
|
+
function tryIndexedOrder(from, order, env, limit) {
|
|
9569
|
+
if (from.type !== "table" || order.expr.type !== "column") return null;
|
|
9570
|
+
const alias = from.alias ?? from.name;
|
|
9571
|
+
const db = env.state.databaseForSchema(from.schema, from.schema ? `${from.schema}.${from.name}` : from.name);
|
|
9572
|
+
let table;
|
|
9573
|
+
try {
|
|
9574
|
+
table = db.getTable(from.name);
|
|
9575
|
+
} catch {
|
|
9576
|
+
return null;
|
|
9577
|
+
}
|
|
9578
|
+
const col = order.expr.name.toLowerCase();
|
|
9579
|
+
for (const indexName of table.indexes) {
|
|
9580
|
+
const index = db.indexes.get(indexName.toLowerCase());
|
|
9581
|
+
if (!index || index.where || index.columns.length !== 1 || index.columns[0].expr) continue;
|
|
9582
|
+
if (index.columns[0].name.toLowerCase() !== col) continue;
|
|
9583
|
+
const desc = order.dir === "DESC";
|
|
9584
|
+
const rows = [];
|
|
9585
|
+
for (const rowid of index.store.orderedRowids(desc)) {
|
|
9586
|
+
const row = table.get(rowid);
|
|
9587
|
+
if (row) rows.push(row);
|
|
9588
|
+
if (limit !== void 0 && rows.length >= limit) break;
|
|
9589
|
+
}
|
|
9590
|
+
return { table, alias, rows, coveredColumns: /* @__PURE__ */ new Set([col]) };
|
|
9591
|
+
}
|
|
9058
9592
|
return null;
|
|
9059
9593
|
}
|
|
9060
9594
|
function compareRowids3(left, right) {
|
|
@@ -9499,6 +10033,7 @@ function executeWith(stmt, env, parent) {
|
|
|
9499
10033
|
function executeSelectCore(stmt, env, parent) {
|
|
9500
10034
|
let scopes;
|
|
9501
10035
|
let skipWhere = false;
|
|
10036
|
+
let skipOrder = false;
|
|
9502
10037
|
if (stmt.from && stmt.where) {
|
|
9503
10038
|
const indexed = tryIndexedTableRows(stmt.from, stmt.where, env);
|
|
9504
10039
|
if (indexed) {
|
|
@@ -9507,6 +10042,28 @@ function executeSelectCore(stmt, env, parent) {
|
|
|
9507
10042
|
} else {
|
|
9508
10043
|
scopes = scanFrom(stmt.from, env, parent);
|
|
9509
10044
|
}
|
|
10045
|
+
} else if (stmt.from && stmt.orderBy.length === 1 && stmt.orderBy[0]) {
|
|
10046
|
+
let take;
|
|
10047
|
+
if (stmt.limit) {
|
|
10048
|
+
const ctx = env.createEvalContext(null, parent);
|
|
10049
|
+
const limitValue = toInteger(evalExpr(stmt.limit.limit, ctx));
|
|
10050
|
+
const offsetValue = stmt.limit.offset ? toInteger(evalExpr(stmt.limit.offset, ctx)) : 0;
|
|
10051
|
+
if (limitValue !== null && offsetValue !== null) {
|
|
10052
|
+
const offset = Math.max(0, Number(offsetValue));
|
|
10053
|
+
const limit = Number(limitValue);
|
|
10054
|
+
take = limit < 0 ? void 0 : offset + limit;
|
|
10055
|
+
}
|
|
10056
|
+
}
|
|
10057
|
+
const ordered = tryIndexedOrder(
|
|
10058
|
+
stmt.from,
|
|
10059
|
+
{ expr: stmt.orderBy[0].expr, dir: stmt.orderBy[0].dir ?? "ASC" },
|
|
10060
|
+
env,
|
|
10061
|
+
take
|
|
10062
|
+
);
|
|
10063
|
+
if (ordered) {
|
|
10064
|
+
scopes = scopesFromTableRows(ordered.table, ordered.rows, ordered.alias, env, parent);
|
|
10065
|
+
skipOrder = true;
|
|
10066
|
+
} else scopes = scanFrom(stmt.from, env, parent);
|
|
9510
10067
|
} else {
|
|
9511
10068
|
scopes = stmt.from ? scanFrom(stmt.from, env, parent) : [{ cells: [] }];
|
|
9512
10069
|
}
|
|
@@ -9580,7 +10137,7 @@ function executeSelectCore(stmt, env, parent) {
|
|
|
9580
10137
|
return true;
|
|
9581
10138
|
});
|
|
9582
10139
|
}
|
|
9583
|
-
if (stmt.orderBy.length > 0) {
|
|
10140
|
+
if (stmt.orderBy.length > 0 && !skipOrder) {
|
|
9584
10141
|
output.sort((left, right) => compareOutput(left, right, stmt.orderBy, columns, env, parent));
|
|
9585
10142
|
}
|
|
9586
10143
|
if (stmt.limit) {
|
|
@@ -10033,8 +10590,9 @@ function windowValue(expr, current, rows, env, parent, namedWindows) {
|
|
|
10033
10590
|
}
|
|
10034
10591
|
}
|
|
10035
10592
|
if (expr.func.type === "aggregate") {
|
|
10036
|
-
const [start2, end2] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd);
|
|
10037
|
-
|
|
10593
|
+
const [start2, end2] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd, orderKeys);
|
|
10594
|
+
const framed2 = frameRows(partition, start2, end2, index, orderKeys, spec.frame?.exclude ?? null);
|
|
10595
|
+
return aggregateValue(expr.func, framed2, env, parent);
|
|
10038
10596
|
}
|
|
10039
10597
|
const name = expr.func.name.toLowerCase();
|
|
10040
10598
|
const args = expr.func.args === "*" ? [] : expr.func.args;
|
|
@@ -10056,12 +10614,14 @@ function windowValue(expr, current, rows, env, parent, namedWindows) {
|
|
|
10056
10614
|
const target = name === "lag" ? index - offset : index + offset;
|
|
10057
10615
|
return values[target]?.[0] ?? evaluated[2] ?? null;
|
|
10058
10616
|
}
|
|
10059
|
-
const [start, end] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd);
|
|
10060
|
-
|
|
10061
|
-
|
|
10617
|
+
const [start, end] = frameBounds(spec, index, partition.length, currentCtx, defaultFrameEnd, orderKeys);
|
|
10618
|
+
const framed = frameRows(partition, start, end, index, orderKeys, spec.frame?.exclude ?? null);
|
|
10619
|
+
const framedValues = framed.map((row) => args.map((arg) => evalExpr(arg, env.createEvalContext(row, parent))));
|
|
10620
|
+
if (name === "first_value") return framedValues[0]?.[0] ?? null;
|
|
10621
|
+
if (name === "last_value") return framedValues[framedValues.length - 1]?.[0] ?? null;
|
|
10062
10622
|
if (name === "nth_value") {
|
|
10063
|
-
const target =
|
|
10064
|
-
return target >=
|
|
10623
|
+
const target = Number(toInteger(evaluated[1] ?? null) ?? 0) - 1;
|
|
10624
|
+
return target >= 0 && target < framedValues.length ? framedValues[target]?.[0] ?? null : null;
|
|
10065
10625
|
}
|
|
10066
10626
|
if (name === "ntile") {
|
|
10067
10627
|
const buckets = Math.max(1, Number(toInteger(evaluated[0] ?? 1) ?? 1));
|
|
@@ -10108,15 +10668,21 @@ function resolveWindow(spec, namedWindows, seen = /* @__PURE__ */ new Set()) {
|
|
|
10108
10668
|
ref: null
|
|
10109
10669
|
};
|
|
10110
10670
|
}
|
|
10111
|
-
function frameBounds(spec, index, length, ctx, defaultFrameEnd) {
|
|
10671
|
+
function frameBounds(spec, index, length, ctx, defaultFrameEnd, orderKeys) {
|
|
10112
10672
|
if (!spec.frame) return [0, spec.orderBy.length > 0 ? defaultFrameEnd : Math.max(0, length - 1)];
|
|
10113
|
-
const
|
|
10673
|
+
const isRangeLike = spec.frame.type === "RANGE" || spec.frame.type === "GROUPS";
|
|
10674
|
+
let peerFirst = index;
|
|
10675
|
+
if (isRangeLike && spec.orderBy.length > 0) {
|
|
10676
|
+
while (peerFirst > 0 && rowsEqual(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
|
|
10677
|
+
}
|
|
10678
|
+
const bound = (item, isStart) => {
|
|
10114
10679
|
switch (item.kind) {
|
|
10115
10680
|
case "unbounded_preceding":
|
|
10116
10681
|
return 0;
|
|
10117
10682
|
case "unbounded_following":
|
|
10118
10683
|
return Math.max(0, length - 1);
|
|
10119
10684
|
case "current_row":
|
|
10685
|
+
if (isRangeLike) return isStart ? peerFirst : defaultFrameEnd;
|
|
10120
10686
|
return index;
|
|
10121
10687
|
case "preceding":
|
|
10122
10688
|
return Math.max(0, index - Number(toInteger(evalExpr(item.expr, ctx)) ?? 0));
|
|
@@ -10126,6 +10692,20 @@ function frameBounds(spec, index, length, ctx, defaultFrameEnd) {
|
|
|
10126
10692
|
};
|
|
10127
10693
|
return [bound(spec.frame.start, true), bound(spec.frame.end, false)];
|
|
10128
10694
|
}
|
|
10695
|
+
function frameRows(partition, start, end, index, orderKeys, exclude) {
|
|
10696
|
+
let peerFirst = index;
|
|
10697
|
+
let peerLast = index;
|
|
10698
|
+
while (peerFirst > 0 && rowsEqual(orderKeys[peerFirst], orderKeys[peerFirst - 1])) peerFirst--;
|
|
10699
|
+
while (peerLast + 1 < partition.length && rowsEqual(orderKeys[peerLast], orderKeys[peerLast + 1])) peerLast++;
|
|
10700
|
+
const rows = [];
|
|
10701
|
+
for (let i = Math.max(0, start); i <= end && i < partition.length; i++) {
|
|
10702
|
+
if (exclude === "current_row" && i === index) continue;
|
|
10703
|
+
if (exclude === "group" && i >= peerFirst && i <= peerLast) continue;
|
|
10704
|
+
if (exclude === "ties" && i >= peerFirst && i <= peerLast && i !== index) continue;
|
|
10705
|
+
rows.push(partition[i]);
|
|
10706
|
+
}
|
|
10707
|
+
return rows;
|
|
10708
|
+
}
|
|
10129
10709
|
function compareScopes(a, b, order, env, parent) {
|
|
10130
10710
|
for (const item of order) {
|
|
10131
10711
|
const left = evalExpr(item.expr, env.createEvalContext(a, parent));
|
|
@@ -10475,11 +11055,12 @@ function executeCreateIndex(stmt, env) {
|
|
|
10475
11055
|
value: row.values.get(normalizeColumnName(column.name)) ?? null
|
|
10476
11056
|
}))
|
|
10477
11057
|
});
|
|
10478
|
-
if (stmt.where &&
|
|
11058
|
+
if (stmt.where && isTruthySql(evalExpr(stmt.where, ctx)) !== true) continue;
|
|
10479
11059
|
index.store.insert(
|
|
10480
|
-
stmt.columns.map(
|
|
10481
|
-
(column)
|
|
10482
|
-
|
|
11060
|
+
stmt.columns.map((column) => {
|
|
11061
|
+
const raw = column.expr ? evalExpr(column.expr, ctx) : row.values.get(normalizeColumnName(column.name)) ?? null;
|
|
11062
|
+
return normalizeForCollation(raw, column.collate ?? "BINARY");
|
|
11063
|
+
}),
|
|
10483
11064
|
row.rowid,
|
|
10484
11065
|
stmt.unique
|
|
10485
11066
|
);
|
|
@@ -10491,7 +11072,7 @@ function executeCreateIndex(stmt, env) {
|
|
|
10491
11072
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
10492
11073
|
}
|
|
10493
11074
|
function executeAlterTable(stmt, env) {
|
|
10494
|
-
const table = env.state.
|
|
11075
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
10495
11076
|
const action = stmt.action;
|
|
10496
11077
|
if (action.kind === "rename_table") {
|
|
10497
11078
|
env.state.renameTable(stmt.table, action.newName);
|
|
@@ -10510,7 +11091,7 @@ function executeAlterTable(stmt, env) {
|
|
|
10510
11091
|
}
|
|
10511
11092
|
for (const constraint of table.constraints) renameConstraintColumn(constraint, action.oldName, action.newName);
|
|
10512
11093
|
for (const name of table.indexes) {
|
|
10513
|
-
const index = env.state.
|
|
11094
|
+
const index = env.state.getWritableIndex(name);
|
|
10514
11095
|
if (index) {
|
|
10515
11096
|
for (const indexed of index.columns)
|
|
10516
11097
|
if (indexed.name.toLowerCase() === action.oldName.toLowerCase()) indexed.name = action.newName;
|
|
@@ -10830,13 +11411,17 @@ function executeFtsDelete(table, rowids) {
|
|
|
10830
11411
|
}
|
|
10831
11412
|
|
|
10832
11413
|
// src/executor/dml.ts
|
|
11414
|
+
function storeColumnValue(table, column, value) {
|
|
11415
|
+
if (table.strict) return applyStrictValue(value, column.typeName ?? "", table.name, column.name);
|
|
11416
|
+
return applyAffinity(value, column.affinity);
|
|
11417
|
+
}
|
|
10833
11418
|
function executeInsert(stmt, env) {
|
|
10834
11419
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
10835
11420
|
return executeVirtualInsert(stmt, env);
|
|
10836
11421
|
}
|
|
10837
11422
|
const fast = tryFastInsert(stmt, env);
|
|
10838
11423
|
if (fast) return fast;
|
|
10839
|
-
const table = env.state.
|
|
11424
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
10840
11425
|
const columnNames = stmt.columns ?? table.columns.map((column) => column.name);
|
|
10841
11426
|
const rowidIndexes = columnNames.map((name, index) => isRowidName3(name) ? index : -1).filter((index) => index >= 0);
|
|
10842
11427
|
if (rowidIndexes.length > 1) throw new SqliteError("duplicate column name: rowid", "other");
|
|
@@ -10861,7 +11446,7 @@ function executeInsert(stmt, env) {
|
|
|
10861
11446
|
const column = table.columns[index];
|
|
10862
11447
|
const suppliedIndex = suppliedIndexes[index];
|
|
10863
11448
|
const value = suppliedIndex >= 0 ? source[suppliedIndex] ?? null : null;
|
|
10864
|
-
values.set(column.nameLower ?? column.name.toLowerCase(),
|
|
11449
|
+
values.set(column.nameLower ?? column.name.toLowerCase(), storeColumnValue(table, column, value));
|
|
10865
11450
|
}
|
|
10866
11451
|
last = table.insert({ values }, { prepared: true, skipValidate: true });
|
|
10867
11452
|
changes++;
|
|
@@ -10877,7 +11462,7 @@ function executeInsert(stmt, env) {
|
|
|
10877
11462
|
if (column.generated) continue;
|
|
10878
11463
|
const suppliedIndex = suppliedIndexes[table.columns.indexOf(column)] ?? -1;
|
|
10879
11464
|
const value = suppliedIndex >= 0 ? source[suppliedIndex] ?? null : column.defaultExpr ? evalExpr(column.defaultExpr, env.createEvalContext()) : null;
|
|
10880
|
-
values.set(column.nameLower ?? column.name.toLowerCase(),
|
|
11465
|
+
values.set(column.nameLower ?? column.name.toLowerCase(), storeColumnValue(table, column, value));
|
|
10881
11466
|
}
|
|
10882
11467
|
for (const column of table.columns) {
|
|
10883
11468
|
if (!column.generated) continue;
|
|
@@ -10901,7 +11486,7 @@ function executeInsert(stmt, env) {
|
|
|
10901
11486
|
})
|
|
10902
11487
|
});
|
|
10903
11488
|
const computed = evalExpr(column.generated.expr, genCtx);
|
|
10904
|
-
values.set(normalizeColumnName(column.name),
|
|
11489
|
+
values.set(normalizeColumnName(column.name), storeColumnValue(table, column, computed));
|
|
10905
11490
|
} else {
|
|
10906
11491
|
values.set(normalizeColumnName(column.name), null);
|
|
10907
11492
|
}
|
|
@@ -10910,7 +11495,7 @@ function executeInsert(stmt, env) {
|
|
|
10910
11495
|
const conflicts = conflictingRows(table, values, env);
|
|
10911
11496
|
if (conflicts.length > 0) {
|
|
10912
11497
|
if (stmt.upsert) {
|
|
10913
|
-
const targetConflicts =
|
|
11498
|
+
const targetConflicts = conflictsForUpsert(table, values, env, stmt.upsert);
|
|
10914
11499
|
if (targetConflicts.length === 0) {
|
|
10915
11500
|
throw new SqliteError(
|
|
10916
11501
|
`UNIQUE constraint failed: ${table.name}`,
|
|
@@ -11004,7 +11589,7 @@ function tryFastInsert(stmt, env) {
|
|
|
11004
11589
|
}
|
|
11005
11590
|
let table;
|
|
11006
11591
|
try {
|
|
11007
|
-
table = env.state.
|
|
11592
|
+
table = env.state.getWritableTable(plan.tableName);
|
|
11008
11593
|
} catch {
|
|
11009
11594
|
return null;
|
|
11010
11595
|
}
|
|
@@ -11055,7 +11640,7 @@ function executeUpdate(stmt, env) {
|
|
|
11055
11640
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
11056
11641
|
return executeVirtualUpdate(stmt, env);
|
|
11057
11642
|
}
|
|
11058
|
-
const table = env.state.
|
|
11643
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
11059
11644
|
const alias = stmt.alias ?? stmt.table;
|
|
11060
11645
|
const candidates = [];
|
|
11061
11646
|
if (stmt.from) {
|
|
@@ -11114,7 +11699,7 @@ function executeDelete(stmt, env) {
|
|
|
11114
11699
|
if (env.state.isVirtualTable(stmt.table)) {
|
|
11115
11700
|
return executeVirtualDelete(stmt, env);
|
|
11116
11701
|
}
|
|
11117
|
-
const table = env.state.
|
|
11702
|
+
const table = env.state.getWritableTable(stmt.table);
|
|
11118
11703
|
const selectedSource = stmt.where === null ? null : tryIndexedTableRows({ type: "table", schema: null, name: stmt.table, alias: stmt.alias }, stmt.where, env);
|
|
11119
11704
|
const selected = [...selectedSource ? selectedSource.rows : table.scan()].filter((row) => {
|
|
11120
11705
|
if (!stmt.where) return true;
|
|
@@ -11148,7 +11733,7 @@ function evaluateSet(items, table, ctx) {
|
|
|
11148
11733
|
if (item.columns.length === 1) {
|
|
11149
11734
|
const column = columnOf(table, item.columns[0]);
|
|
11150
11735
|
if (column.generated) throw new SqliteError(`cannot UPDATE generated column "${column.name}"`, "misuse");
|
|
11151
|
-
updates.set(normalizeColumnName(column.name), evalExpr(item.expr, ctx));
|
|
11736
|
+
updates.set(normalizeColumnName(column.name), storeColumnValue(table, column, evalExpr(item.expr, ctx)));
|
|
11152
11737
|
continue;
|
|
11153
11738
|
}
|
|
11154
11739
|
if (item.expr.type !== "row" || item.expr.values.length !== item.columns.length) {
|
|
@@ -11162,7 +11747,7 @@ function evaluateSet(items, table, ctx) {
|
|
|
11162
11747
|
if (column.generated) throw new SqliteError(`cannot UPDATE generated column "${column.name}"`, "misuse");
|
|
11163
11748
|
updates.set(
|
|
11164
11749
|
normalizeColumnName(column.name),
|
|
11165
|
-
evalExpr(item.expr.type === "row" ? item.expr.values[index] : item.expr, ctx)
|
|
11750
|
+
storeColumnValue(table, column, evalExpr(item.expr.type === "row" ? item.expr.values[index] : item.expr, ctx))
|
|
11166
11751
|
);
|
|
11167
11752
|
});
|
|
11168
11753
|
}
|
|
@@ -11189,7 +11774,7 @@ function updateOne(table, row, updates, env) {
|
|
|
11189
11774
|
});
|
|
11190
11775
|
merged.set(
|
|
11191
11776
|
normalizeColumnName(column.name),
|
|
11192
|
-
|
|
11777
|
+
storeColumnValue(table, column, evalExpr(column.generated.expr, genCtx))
|
|
11193
11778
|
);
|
|
11194
11779
|
}
|
|
11195
11780
|
after = table.update(row.rowid, merged);
|
|
@@ -11220,20 +11805,23 @@ function validateRow(table, row, env) {
|
|
|
11220
11805
|
);
|
|
11221
11806
|
}
|
|
11222
11807
|
function addIndexes(table, row, env) {
|
|
11223
|
-
const
|
|
11808
|
+
const db = env.state.databaseForTable(table);
|
|
11224
11809
|
for (const name of table.indexes) {
|
|
11225
|
-
const
|
|
11226
|
-
if (!
|
|
11810
|
+
const found = db.indexes.get(name.toLowerCase());
|
|
11811
|
+
if (!found) continue;
|
|
11812
|
+
const index = db.ensureWritableIndex(found);
|
|
11227
11813
|
if (index.where && isTruthySql(evalExpr(index.where, env.createEvalContext(scopeFor(table, row, table.name, env)))) !== true)
|
|
11228
11814
|
continue;
|
|
11229
|
-
index.store.insert(indexValues(index.columns, row), row.rowid, index.unique);
|
|
11815
|
+
index.store.insert(indexValues(index.columns, row, env, table), row.rowid, index.unique);
|
|
11230
11816
|
}
|
|
11231
11817
|
}
|
|
11232
11818
|
function removeIndexes(table, row, env) {
|
|
11233
|
-
const
|
|
11819
|
+
const db = env.state.databaseForTable(table);
|
|
11234
11820
|
for (const name of table.indexes) {
|
|
11235
|
-
const
|
|
11236
|
-
if (
|
|
11821
|
+
const found = db.indexes.get(name.toLowerCase());
|
|
11822
|
+
if (!found) continue;
|
|
11823
|
+
const index = db.ensureWritableIndex(found);
|
|
11824
|
+
index.store.remove(indexValues(index.columns, row, env, table), row.rowid);
|
|
11237
11825
|
}
|
|
11238
11826
|
}
|
|
11239
11827
|
function removeOne(table, row, env) {
|
|
@@ -11266,6 +11854,27 @@ function conflictsMatchingTarget(table, values, env, targetColumns) {
|
|
|
11266
11854
|
}
|
|
11267
11855
|
return conflictsForSets(table, values, matchingSets, env);
|
|
11268
11856
|
}
|
|
11857
|
+
function conflictsForUpsert(table, values, env, upsert) {
|
|
11858
|
+
if (!upsert.targetColumns) return conflictingRows(table, values, env);
|
|
11859
|
+
const exprs = upsert.targetExprs;
|
|
11860
|
+
const hasExpr = exprs?.some((expr) => expr.type !== "column") ?? false;
|
|
11861
|
+
if (hasExpr && exprs) {
|
|
11862
|
+
const db = env.state.databaseForTable(table);
|
|
11863
|
+
for (const indexName of table.indexes) {
|
|
11864
|
+
const index = db.indexes.get(indexName.toLowerCase());
|
|
11865
|
+
if (!index?.unique || index.columns.length !== exprs.length) continue;
|
|
11866
|
+
const matches = index.columns.every((column, i) => {
|
|
11867
|
+
const expr = exprs[i];
|
|
11868
|
+
if (column.expr) return exprEquals(column.expr, expr);
|
|
11869
|
+
return expr.type === "column" && expr.name.toLowerCase() === column.name.toLowerCase();
|
|
11870
|
+
});
|
|
11871
|
+
if (!matches) continue;
|
|
11872
|
+
if (upsert.targetWhere && index.where && !exprEquals(upsert.targetWhere, index.where)) continue;
|
|
11873
|
+
return conflictsForSets(table, values, [index.columns], env);
|
|
11874
|
+
}
|
|
11875
|
+
}
|
|
11876
|
+
return conflictsMatchingTarget(table, values, env, upsert.targetColumns.filter(Boolean));
|
|
11877
|
+
}
|
|
11269
11878
|
function uniqueColumnSets(table, env) {
|
|
11270
11879
|
const sets = [];
|
|
11271
11880
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -11277,7 +11886,7 @@ function uniqueColumnSets(table, env) {
|
|
|
11277
11886
|
};
|
|
11278
11887
|
for (const indexName of table.indexes) {
|
|
11279
11888
|
const index = env.state.databaseForTable(table).indexes.get(indexName.toLowerCase());
|
|
11280
|
-
if (index?.unique
|
|
11889
|
+
if (index?.unique) remember(index.columns);
|
|
11281
11890
|
}
|
|
11282
11891
|
const pk = table.integerPkColumn();
|
|
11283
11892
|
if (pk) remember([{ name: pk.name, collate: pk.collate, order: null }]);
|
|
@@ -11303,9 +11912,7 @@ function conflictsForSets(table, values, sets, env) {
|
|
|
11303
11912
|
};
|
|
11304
11913
|
const db = env.state.databaseForTable(table);
|
|
11305
11914
|
for (const columns of sets) {
|
|
11306
|
-
const desired = columns
|
|
11307
|
-
(column) => normalizeForCollation(values.get(normalizeColumnName(column.name)) ?? null, column.collate ?? "BINARY")
|
|
11308
|
-
);
|
|
11915
|
+
const desired = indexValuesFromMap(columns, values, table, env);
|
|
11309
11916
|
if (desired.some((value) => value === null)) continue;
|
|
11310
11917
|
if (columns.length === 1) {
|
|
11311
11918
|
const pk = table.integerPkColumn();
|
|
@@ -11318,12 +11925,13 @@ function conflictsForSets(table, values, sets, env) {
|
|
|
11318
11925
|
let usedIndex = false;
|
|
11319
11926
|
for (const indexName of table.indexes) {
|
|
11320
11927
|
const index = db.indexes.get(indexName.toLowerCase());
|
|
11321
|
-
if (!index?.unique
|
|
11928
|
+
if (!index?.unique) continue;
|
|
11322
11929
|
if (index.columns.length !== columns.length) continue;
|
|
11323
11930
|
if (!index.columns.every((column, i) => normalizeColumnName(column.name) === normalizeColumnName(columns[i].name))) {
|
|
11324
11931
|
continue;
|
|
11325
11932
|
}
|
|
11326
11933
|
usedIndex = true;
|
|
11934
|
+
if (index.where && !indexWhereMatches(index.where, values, table, env)) continue;
|
|
11327
11935
|
for (const rowid of index.store.lookup(desired)) {
|
|
11328
11936
|
const row = table.get(rowid);
|
|
11329
11937
|
if (row) push(row);
|
|
@@ -11332,36 +11940,63 @@ function conflictsForSets(table, values, sets, env) {
|
|
|
11332
11940
|
}
|
|
11333
11941
|
if (usedIndex) continue;
|
|
11334
11942
|
for (const row of table.scan()) {
|
|
11335
|
-
const existing = indexValues(columns, row);
|
|
11943
|
+
const existing = indexValues(columns, row, env, table);
|
|
11336
11944
|
if (desired.every((value, index) => compareSql(value, existing[index] ?? null) === 0)) push(row);
|
|
11337
11945
|
}
|
|
11338
11946
|
}
|
|
11339
11947
|
return found;
|
|
11340
11948
|
}
|
|
11341
|
-
function indexValues(columns, row) {
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11949
|
+
function indexValues(columns, row, env, table) {
|
|
11950
|
+
if (!env || !table) {
|
|
11951
|
+
return columns.map(
|
|
11952
|
+
(column) => normalizeForCollation(row.values.get(normalizeColumnName(column.name)) ?? null, column.collate ?? "BINARY")
|
|
11953
|
+
);
|
|
11954
|
+
}
|
|
11955
|
+
const ctx = env.createEvalContext(scopeFor(table, row, table.name, env));
|
|
11956
|
+
return columns.map((column) => {
|
|
11957
|
+
const raw = column.expr ? evalExpr(column.expr, ctx) : row.values.get(normalizeColumnName(column.name)) ?? null;
|
|
11958
|
+
return normalizeForCollation(raw, column.collate ?? "BINARY");
|
|
11959
|
+
});
|
|
11960
|
+
}
|
|
11961
|
+
function indexValuesFromMap(columns, values, table, env) {
|
|
11962
|
+
const fake = { rowid: 0, values };
|
|
11963
|
+
return indexValues(columns, fake, env, table);
|
|
11964
|
+
}
|
|
11965
|
+
function indexWhereMatches(where, values, table, env) {
|
|
11966
|
+
const fake = { rowid: 0, values };
|
|
11967
|
+
return isTruthySql(evalExpr(where, env.createEvalContext(scopeFor(table, fake, table.name, env)))) === true;
|
|
11345
11968
|
}
|
|
11346
11969
|
function checkForeignKeys(table, row, env) {
|
|
11347
11970
|
if (!env.state.foreignKeysEnabled) return;
|
|
11348
|
-
const references = [];
|
|
11349
11971
|
for (const constraint of table.constraints) {
|
|
11350
|
-
if (constraint.type
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
|
|
11354
|
-
|
|
11355
|
-
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11972
|
+
if (constraint.type !== "foreign_key") continue;
|
|
11973
|
+
if (fkIsDeferred(constraint, env)) continue;
|
|
11974
|
+
assertForeignKeySatisfied(row, constraint, env);
|
|
11975
|
+
}
|
|
11976
|
+
}
|
|
11977
|
+
function fkIsDeferred(constraint, env) {
|
|
11978
|
+
return env.transactions.inTransaction && constraint.initiallyDeferred;
|
|
11979
|
+
}
|
|
11980
|
+
function assertForeignKeySatisfied(row, constraint, env) {
|
|
11981
|
+
const values = constraint.columns.map((name) => row.values.get(normalizeColumnName(name)) ?? null);
|
|
11982
|
+
if (values.some((value) => value === null)) return;
|
|
11983
|
+
const parent = env.state.getTable(constraint.refTable);
|
|
11984
|
+
const parentColumns = constraint.refColumns ?? parent.columns.filter((column) => column.primaryKey).map((column) => column.name);
|
|
11985
|
+
if (![...parent.scan()].some(
|
|
11986
|
+
(candidate) => values.every((value, index) => {
|
|
11987
|
+
const parentColumn = parentColumns[index];
|
|
11988
|
+
return parentColumn !== void 0 && compareSql(value, candidate.values.get(normalizeColumnName(parentColumn)) ?? null) === 0;
|
|
11989
|
+
})
|
|
11990
|
+
)) {
|
|
11991
|
+
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
11992
|
+
}
|
|
11993
|
+
}
|
|
11994
|
+
function checkDeferredForeignKeys(env) {
|
|
11995
|
+
if (!env.state.foreignKeysEnabled) return;
|
|
11996
|
+
for (const table of env.state.tables.values()) {
|
|
11997
|
+
for (const constraint of table.constraints) {
|
|
11998
|
+
if (constraint.type !== "foreign_key" || !constraint.initiallyDeferred) continue;
|
|
11999
|
+
for (const row of table.scan()) assertForeignKeySatisfied(row, constraint, env);
|
|
11365
12000
|
}
|
|
11366
12001
|
}
|
|
11367
12002
|
}
|
|
@@ -11393,8 +12028,9 @@ function applyReferentialDelete(parent, row, env) {
|
|
|
11393
12028
|
} else if (constraint.onDelete === "SET DEFAULT") {
|
|
11394
12029
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
11395
12030
|
changes += 1 + updated.cascaded;
|
|
11396
|
-
} else
|
|
12031
|
+
} else if (!fkIsDeferred(constraint, env)) {
|
|
11397
12032
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
12033
|
+
}
|
|
11398
12034
|
}
|
|
11399
12035
|
}
|
|
11400
12036
|
}
|
|
@@ -11435,7 +12071,7 @@ function applyReferentialUpdate(parent, before, after, env) {
|
|
|
11435
12071
|
} else if (constraint.onUpdate === "SET DEFAULT") {
|
|
11436
12072
|
const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
|
|
11437
12073
|
changes += 1 + updated.cascaded;
|
|
11438
|
-
} else {
|
|
12074
|
+
} else if (!fkIsDeferred(constraint, env)) {
|
|
11439
12075
|
throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
|
|
11440
12076
|
}
|
|
11441
12077
|
}
|
|
@@ -11901,6 +12537,7 @@ function executeStatement(stmt, env) {
|
|
|
11901
12537
|
env.transactions.begin();
|
|
11902
12538
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11903
12539
|
case "commit":
|
|
12540
|
+
checkDeferredForeignKeys(env);
|
|
11904
12541
|
env.transactions.commit();
|
|
11905
12542
|
return emptyResult(0, env.state.lastInsertRowid);
|
|
11906
12543
|
case "rollback":
|
|
@@ -11947,7 +12584,8 @@ function executeAnalyze(stmt, env) {
|
|
|
11947
12584
|
],
|
|
11948
12585
|
constraints: [],
|
|
11949
12586
|
asSelect: null,
|
|
11950
|
-
withoutRowid: false
|
|
12587
|
+
withoutRowid: false,
|
|
12588
|
+
strict: false
|
|
11951
12589
|
},
|
|
11952
12590
|
"CREATE TABLE sqlite_stat1(tbl,idx,stat)"
|
|
11953
12591
|
);
|
|
@@ -11990,38 +12628,85 @@ function executeAnalyze(stmt, env) {
|
|
|
11990
12628
|
}
|
|
11991
12629
|
|
|
11992
12630
|
// src/api/statement.ts
|
|
11993
|
-
var Statement = class {
|
|
12631
|
+
var Statement = class _Statement {
|
|
11994
12632
|
constructor(database, sql, statements) {
|
|
11995
12633
|
this.database = database;
|
|
11996
12634
|
this.sql = sql;
|
|
11997
12635
|
this.statements = statements;
|
|
12636
|
+
this.schemaVersion = database.state.schemaVersion;
|
|
11998
12637
|
}
|
|
11999
12638
|
database;
|
|
12000
12639
|
sql;
|
|
12001
|
-
statements;
|
|
12002
12640
|
bound = [];
|
|
12003
12641
|
namedPlan = null;
|
|
12004
12642
|
env = null;
|
|
12643
|
+
statements;
|
|
12644
|
+
schemaVersion;
|
|
12645
|
+
/**
|
|
12646
|
+
* Construct a {@link Statement} for {@link Database.prepare}.
|
|
12647
|
+
* @internal
|
|
12648
|
+
*/
|
|
12649
|
+
static create(database, sql, statements) {
|
|
12650
|
+
return new _Statement(database, sql, statements);
|
|
12651
|
+
}
|
|
12652
|
+
/**
|
|
12653
|
+
* Store parameters for later {@link run} / {@link all} / {@link get} / {@link result}.
|
|
12654
|
+
*
|
|
12655
|
+
* Supports positional `?` / `?NNN` and named `:name` / `@name` / `$name` placeholders.
|
|
12656
|
+
*
|
|
12657
|
+
* @param params - Values to bind, in placeholder order.
|
|
12658
|
+
* @returns `this` for chaining.
|
|
12659
|
+
*/
|
|
12005
12660
|
bind(...params) {
|
|
12006
12661
|
this.bound = [...params];
|
|
12007
12662
|
return this;
|
|
12008
12663
|
}
|
|
12664
|
+
/**
|
|
12665
|
+
* Execute for side effects (INSERT / UPDATE / DELETE / DDL).
|
|
12666
|
+
*
|
|
12667
|
+
* @param params - If provided, override the last {@link bind}; otherwise bound values are used.
|
|
12668
|
+
* @returns Mutation counters for this execution.
|
|
12669
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12670
|
+
*/
|
|
12009
12671
|
run(...params) {
|
|
12010
12672
|
const result = this.execute(params.length > 0 ? params : this.bound, { named: false });
|
|
12011
12673
|
return { changes: result.changes, lastInsertRowid: result.lastInsertRowid };
|
|
12012
12674
|
}
|
|
12675
|
+
/**
|
|
12676
|
+
* Execute and return every result row as an object keyed by column name.
|
|
12677
|
+
*
|
|
12678
|
+
* @typeParam T - Row shape. Defaults to {@link QueryRow}.
|
|
12679
|
+
* @param params - If provided, override the last {@link bind}; otherwise bound values are used.
|
|
12680
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12681
|
+
*/
|
|
12013
12682
|
all(...params) {
|
|
12014
12683
|
return this.execute(params.length > 0 ? params : this.bound, { named: true }).rows;
|
|
12015
12684
|
}
|
|
12016
|
-
/**
|
|
12685
|
+
/**
|
|
12686
|
+
* Execute and return the full {@link ResultSet}, including column names.
|
|
12687
|
+
*
|
|
12688
|
+
* Use this when you need metadata for an empty result (column names with zero rows).
|
|
12689
|
+
* {@link all} only returns row objects.
|
|
12690
|
+
*
|
|
12691
|
+
* @param params - If provided, override the last {@link bind}; otherwise bound values are used.
|
|
12692
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12693
|
+
*/
|
|
12017
12694
|
result(...params) {
|
|
12018
12695
|
return this.execute(params.length > 0 ? params : this.bound, { named: true });
|
|
12019
12696
|
}
|
|
12697
|
+
/**
|
|
12698
|
+
* Execute and return the first row, or `undefined` if there are no rows.
|
|
12699
|
+
*
|
|
12700
|
+
* @typeParam T - Row shape. Defaults to {@link QueryRow}.
|
|
12701
|
+
* @param params - If provided, override the last {@link bind}; otherwise bound values are used.
|
|
12702
|
+
* @throws {SqliteError} If the database is closed, the statement is empty, or execution fails.
|
|
12703
|
+
*/
|
|
12020
12704
|
get(...params) {
|
|
12021
12705
|
return this.execute(params.length > 0 ? params : this.bound, { named: true, maxRows: 1 }).rows[0];
|
|
12022
12706
|
}
|
|
12023
12707
|
execute(params, options) {
|
|
12024
12708
|
this.database.assertOpen();
|
|
12709
|
+
this.reprepareIfSchemaChanged();
|
|
12025
12710
|
if (this.statements.length === 0) throw new SqliteError("empty statement", "misuse");
|
|
12026
12711
|
const env = this.obtainEnv(params);
|
|
12027
12712
|
env.maxRows = options?.maxRows ?? Number.POSITIVE_INFINITY;
|
|
@@ -12051,6 +12736,13 @@ var Statement = class {
|
|
|
12051
12736
|
});
|
|
12052
12737
|
return this.env;
|
|
12053
12738
|
}
|
|
12739
|
+
reprepareIfSchemaChanged() {
|
|
12740
|
+
if (this.schemaVersion === this.database.state.schemaVersion) return;
|
|
12741
|
+
this.statements = parse(this.sql);
|
|
12742
|
+
this.env = null;
|
|
12743
|
+
this.namedPlan = null;
|
|
12744
|
+
this.schemaVersion = this.database.state.schemaVersion;
|
|
12745
|
+
}
|
|
12054
12746
|
bindNamed(env, params) {
|
|
12055
12747
|
this.namedPlan ??= planNamedParameters(this.sql);
|
|
12056
12748
|
for (const item of this.namedPlan.named) {
|
|
@@ -12082,31 +12774,81 @@ function planNamedParameters(sql) {
|
|
|
12082
12774
|
|
|
12083
12775
|
// src/api/database.ts
|
|
12084
12776
|
var Database = class {
|
|
12777
|
+
/** @internal Engine catalog, tables, and mutation counters. */
|
|
12085
12778
|
state = new DatabaseState();
|
|
12779
|
+
/** Seed used to construct the PRNG when `options.prng` is omitted. */
|
|
12086
12780
|
seed;
|
|
12781
|
+
/**
|
|
12782
|
+
* PRNG backing `random()` / `randomblob()` and related builtins.
|
|
12783
|
+
* Prefer passing `seed` or `prng` to the constructor.
|
|
12784
|
+
* @internal
|
|
12785
|
+
*/
|
|
12087
12786
|
prng;
|
|
12787
|
+
/**
|
|
12788
|
+
* Clock used by `date('now')` / `datetime('now')` / `CURRENT_TIMESTAMP`.
|
|
12789
|
+
* Prefer passing `now` to the constructor.
|
|
12790
|
+
* @internal
|
|
12791
|
+
*/
|
|
12088
12792
|
now;
|
|
12793
|
+
/** @internal Transaction / savepoint manager. */
|
|
12089
12794
|
transactions;
|
|
12090
12795
|
closed = false;
|
|
12091
12796
|
transactionSequence = 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
12804
|
this.prng = options.prng ?? new Prng(this.seed);
|
|
12095
12805
|
this.now = resolveClock(options.now);
|
|
12096
12806
|
this.transactions = new TransactionManager(this.state, this.prng);
|
|
12097
12807
|
}
|
|
12808
|
+
/**
|
|
12809
|
+
* Execute SQL for its side effects (DDL/DML). Multiple statements are allowed.
|
|
12810
|
+
*
|
|
12811
|
+
* @param sql - SQL to run (semicolon-separated statements are ok).
|
|
12812
|
+
* @param params - Bound parameters for `?` / `:name` placeholders.
|
|
12813
|
+
* @throws {SqliteError} If the database is closed or the SQL fails.
|
|
12814
|
+
*/
|
|
12098
12815
|
exec(sql, params = []) {
|
|
12099
12816
|
this.assertOpen();
|
|
12100
|
-
|
|
12817
|
+
Statement.create(this, sql, parse(sql)).run(...params);
|
|
12101
12818
|
}
|
|
12819
|
+
/**
|
|
12820
|
+
* Execute a query and return all rows as objects keyed by column name.
|
|
12821
|
+
*
|
|
12822
|
+
* @typeParam T - Row shape. Defaults to {@link QueryRow}.
|
|
12823
|
+
* @param sql - SQL SELECT (or any statement that produces a result set).
|
|
12824
|
+
* @param params - Bound parameters for `?` / `:name` placeholders.
|
|
12825
|
+
* @returns All result rows.
|
|
12826
|
+
* @throws {SqliteError} If the database is closed or the SQL fails.
|
|
12827
|
+
*/
|
|
12102
12828
|
query(sql, params = []) {
|
|
12103
12829
|
this.assertOpen();
|
|
12104
|
-
return
|
|
12830
|
+
return Statement.create(this, sql, parse(sql)).all(...params);
|
|
12105
12831
|
}
|
|
12832
|
+
/**
|
|
12833
|
+
* Compile `sql` into a reusable {@link Statement}.
|
|
12834
|
+
*
|
|
12835
|
+
* @param sql - SQL to prepare.
|
|
12836
|
+
* @throws {SqliteError} If the database is closed or `sql` cannot be parsed.
|
|
12837
|
+
*/
|
|
12106
12838
|
prepare(sql) {
|
|
12107
12839
|
this.assertOpen();
|
|
12108
|
-
return
|
|
12840
|
+
return Statement.create(this, sql, parse(sql));
|
|
12109
12841
|
}
|
|
12842
|
+
/**
|
|
12843
|
+
* Run `fn` inside a transaction. Commits on success; rolls back if `fn` throws.
|
|
12844
|
+
*
|
|
12845
|
+
* Nested calls use SAVEPOINTs so an inner failure does not abort the outer
|
|
12846
|
+
* transaction.
|
|
12847
|
+
*
|
|
12848
|
+
* @param fn - Work to run while the transaction is open.
|
|
12849
|
+
* @returns The value returned by `fn`.
|
|
12850
|
+
* @throws {SqliteError} If the database is closed. Re-throws whatever `fn` throws after rollback.
|
|
12851
|
+
*/
|
|
12110
12852
|
transaction(fn) {
|
|
12111
12853
|
this.assertOpen();
|
|
12112
12854
|
if (!this.transactions.inTransaction) {
|
|
@@ -12132,6 +12874,13 @@ var Database = class {
|
|
|
12132
12874
|
throw error;
|
|
12133
12875
|
}
|
|
12134
12876
|
}
|
|
12877
|
+
/**
|
|
12878
|
+
* Serialize schema, rows, PRNG state, and clock into a custom snapshot blob.
|
|
12879
|
+
*
|
|
12880
|
+
* This is not a `.sqlite` file. Restore it with {@link restore}.
|
|
12881
|
+
*
|
|
12882
|
+
* @throws {SqliteError} If the database is closed.
|
|
12883
|
+
*/
|
|
12135
12884
|
snapshot() {
|
|
12136
12885
|
this.assertOpen();
|
|
12137
12886
|
return encodeDatabaseState(this.state, {
|
|
@@ -12139,6 +12888,14 @@ var Database = class {
|
|
|
12139
12888
|
nowMs: this.now().getTime()
|
|
12140
12889
|
});
|
|
12141
12890
|
}
|
|
12891
|
+
/**
|
|
12892
|
+
* Replace this database's contents with a blob from {@link snapshot}.
|
|
12893
|
+
*
|
|
12894
|
+
* Restores PRNG state and the clock when the snapshot includes them (v2).
|
|
12895
|
+
*
|
|
12896
|
+
* @param snapshot - Bytes previously returned by {@link snapshot}.
|
|
12897
|
+
* @throws {SqliteError} If the database is closed, a transaction is open, or the blob is invalid.
|
|
12898
|
+
*/
|
|
12142
12899
|
restore(snapshot) {
|
|
12143
12900
|
this.assertOpen();
|
|
12144
12901
|
if (this.transactions.inTransaction) throw new SqliteError("cannot restore during a transaction", "transaction");
|
|
@@ -12150,19 +12907,39 @@ var Database = class {
|
|
|
12150
12907
|
this.now = fixedClock(new Date(decoded.runtime.nowMs));
|
|
12151
12908
|
}
|
|
12152
12909
|
}
|
|
12910
|
+
/**
|
|
12911
|
+
* Close the database. Further SQL throws {@link SqliteError}. Idempotent.
|
|
12912
|
+
*
|
|
12913
|
+
* Rolls back an open transaction, if any.
|
|
12914
|
+
*/
|
|
12153
12915
|
close() {
|
|
12154
12916
|
if (this.closed) return;
|
|
12155
12917
|
if (this.transactions.inTransaction) this.transactions.rollback();
|
|
12156
12918
|
this.closed = true;
|
|
12157
12919
|
}
|
|
12920
|
+
/**
|
|
12921
|
+
* Rows changed by the most recent INSERT / UPDATE / DELETE (SQLite `changes()`).
|
|
12922
|
+
*
|
|
12923
|
+
* @throws {SqliteError} If the database is closed.
|
|
12924
|
+
*/
|
|
12158
12925
|
get changes() {
|
|
12159
12926
|
this.assertOpen();
|
|
12160
12927
|
return this.state.changes;
|
|
12161
12928
|
}
|
|
12929
|
+
/**
|
|
12930
|
+
* Rowid of the most recent INSERT (SQLite `last_insert_rowid()`).
|
|
12931
|
+
*
|
|
12932
|
+
* @throws {SqliteError} If the database is closed.
|
|
12933
|
+
*/
|
|
12162
12934
|
get lastInsertRowid() {
|
|
12163
12935
|
this.assertOpen();
|
|
12164
12936
|
return this.state.lastInsertRowid;
|
|
12165
12937
|
}
|
|
12938
|
+
/**
|
|
12939
|
+
* Throw if {@link close} has already been called.
|
|
12940
|
+
* @internal
|
|
12941
|
+
* @throws {SqliteError} If the database is closed.
|
|
12942
|
+
*/
|
|
12166
12943
|
assertOpen() {
|
|
12167
12944
|
if (this.closed) throw new SqliteError("Database is closed", "misuse");
|
|
12168
12945
|
}
|
|
@@ -12172,11 +12949,13 @@ export {
|
|
|
12172
12949
|
DEFAULT_NOW,
|
|
12173
12950
|
Database,
|
|
12174
12951
|
Prng,
|
|
12952
|
+
SqlJsonText,
|
|
12175
12953
|
SqlReal,
|
|
12176
12954
|
SqliteError,
|
|
12177
12955
|
Statement,
|
|
12178
12956
|
affinityFromTypeName,
|
|
12179
12957
|
applyAffinity,
|
|
12958
|
+
asSqlJsonText,
|
|
12180
12959
|
asSqlReal,
|
|
12181
12960
|
canonicalizeNumber,
|
|
12182
12961
|
cloneSqlValue,
|
|
@@ -12188,6 +12967,7 @@ export {
|
|
|
12188
12967
|
evalExpr,
|
|
12189
12968
|
fixedClock,
|
|
12190
12969
|
globMatch,
|
|
12970
|
+
isSqlJsonText,
|
|
12191
12971
|
isSqlReal,
|
|
12192
12972
|
isTruthySql,
|
|
12193
12973
|
likeMatch,
|