@crvouga/sqlite-mem 1.1.1 → 1.1.2
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/README.md +2 -0
- package/dist/ast/nodes.d.ts +9 -1
- package/dist/index.js +43 -2
- package/dist/index.js.map +2 -2
- package/dist/lexer/tokenize.d.ts +1 -1
- package/dist/unstable.js +23 -0
- package/dist/unstable.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -218,6 +218,8 @@ Goal: drop-in SQL behavior vs SQLite **3.51.0**. Full matrix: [COMPATIBILITY.md]
|
|
|
218
218
|
- `INDEXED BY` / `NOT INDEXED` — parsed and discarded
|
|
219
219
|
- Unknown statement `PRAGMA` succeeds with an empty result (SQLite-like). All oracle-exposed `pragma_*` eponymous TVFs are supported (`SELECT * FROM pragma_table_info('t')`, bare `FROM pragma_database_list`, …), including **correlated** args such as `FROM table_list AS tl, pragma_table_info(tl.name) AS p` (Kysely SQLite introspector). Storage/journal getters return bun `:memory:`-compatible defaults.
|
|
220
220
|
|
|
221
|
+
**Also supported (oracle-parity):** boolean literals **`TRUE` / `FALSE`** (any case → integers `1` / `0`) and **`IS [NOT] TRUE` / `IS [NOT] FALSE`** (SQLite truthiness, including NULL). A column named `true`/`false` shadows the literal.
|
|
222
|
+
|
|
221
223
|
## Common pitfalls
|
|
222
224
|
|
|
223
225
|
1. **Do not `await`** — the API is sync.
|
package/dist/ast/nodes.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { SqlValue } from "../types/value.js";
|
|
|
2
2
|
export type BinaryOp = "+" | "-" | "*" | "/" | "%" | "||" | "=" | "==" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "AND" | "OR" | "IS" | "IS NOT" | "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" | "LIKE" | "NOT LIKE" | "GLOB" | "NOT GLOB" | "IN" | "NOT IN" | "MATCH" | "->" | "->>" | "&" | "|" | "<<" | ">>";
|
|
3
3
|
export type UnaryOp = "+" | "-" | "~" | "NOT";
|
|
4
4
|
/** Parsed SQL expression AST (literals, operators, functions, subqueries, …). */
|
|
5
|
-
export type Expr = LiteralExpr | NullExpr | ColumnRefExpr | UnaryExpr | BinaryExpr | BetweenExpr | InExpr | LikeExpr | FunctionExpr | AggregateExpr | WindowExpr | CaseExpr | CastExpr | ExistsExpr | SubqueryExpr | ParameterExpr | RowExpr | CollateExpr;
|
|
5
|
+
export type Expr = LiteralExpr | NullExpr | ColumnRefExpr | UnaryExpr | BinaryExpr | BetweenExpr | InExpr | LikeExpr | IsBoolExpr | FunctionExpr | AggregateExpr | WindowExpr | CaseExpr | CastExpr | ExistsExpr | SubqueryExpr | ParameterExpr | RowExpr | CollateExpr;
|
|
6
6
|
export interface LiteralExpr {
|
|
7
7
|
type: "literal";
|
|
8
8
|
value: SqlValue;
|
|
@@ -28,6 +28,14 @@ export interface BinaryExpr {
|
|
|
28
28
|
left: Expr;
|
|
29
29
|
right: Expr;
|
|
30
30
|
}
|
|
31
|
+
/** `expr IS [NOT] TRUE` / `expr IS [NOT] FALSE` (truthiness, not equality to 1/0). */
|
|
32
|
+
export interface IsBoolExpr {
|
|
33
|
+
type: "is_bool";
|
|
34
|
+
expr: Expr;
|
|
35
|
+
not: boolean;
|
|
36
|
+
/** `true` for TRUE, `false` for FALSE. */
|
|
37
|
+
sense: boolean;
|
|
38
|
+
}
|
|
31
39
|
export interface BetweenExpr {
|
|
32
40
|
type: "between";
|
|
33
41
|
not: boolean;
|
package/dist/index.js
CHANGED
|
@@ -91,6 +91,7 @@ var KEYWORDS = {
|
|
|
91
91
|
FOREIGN: "FOREIGN",
|
|
92
92
|
FROM: "FROM",
|
|
93
93
|
FULL: "FULL",
|
|
94
|
+
FALSE: "FALSE",
|
|
94
95
|
GENERATED: "GENERATED",
|
|
95
96
|
GLOB: "GLOB",
|
|
96
97
|
GROUP: "GROUP",
|
|
@@ -165,6 +166,7 @@ var KEYWORDS = {
|
|
|
165
166
|
TO: "TO",
|
|
166
167
|
TRANSACTION: "TRANSACTION",
|
|
167
168
|
TRIGGER: "TRIGGER",
|
|
169
|
+
TRUE: "TRUE",
|
|
168
170
|
UNBOUNDED: "UNBOUNDED",
|
|
169
171
|
UNION: "UNION",
|
|
170
172
|
UNIQUE: "UNIQUE",
|
|
@@ -519,6 +521,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
519
521
|
"FOREIGN",
|
|
520
522
|
"FROM",
|
|
521
523
|
"FULL",
|
|
524
|
+
"FALSE",
|
|
522
525
|
"GENERATED",
|
|
523
526
|
"GLOB",
|
|
524
527
|
"GROUP",
|
|
@@ -593,6 +596,7 @@ var IDENT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
593
596
|
"TO",
|
|
594
597
|
"TRANSACTION",
|
|
595
598
|
"TRIGGER",
|
|
599
|
+
"TRUE",
|
|
596
600
|
"UNBOUNDED",
|
|
597
601
|
"UNION",
|
|
598
602
|
"UNIQUE",
|
|
@@ -1939,6 +1943,14 @@ var Parser = class {
|
|
|
1939
1943
|
continue;
|
|
1940
1944
|
}
|
|
1941
1945
|
const not = this.match("NOT");
|
|
1946
|
+
if (this.match("TRUE")) {
|
|
1947
|
+
left = { type: "is_bool", expr: left, not, sense: true };
|
|
1948
|
+
continue;
|
|
1949
|
+
}
|
|
1950
|
+
if (this.match("FALSE")) {
|
|
1951
|
+
left = { type: "is_bool", expr: left, not, sense: false };
|
|
1952
|
+
continue;
|
|
1953
|
+
}
|
|
1942
1954
|
const right2 = this.parseIsRhs();
|
|
1943
1955
|
left = { type: "binary", op: not ? "IS NOT" : "IS", left, right: right2 };
|
|
1944
1956
|
continue;
|
|
@@ -5527,6 +5539,8 @@ function explicitCollation(expr) {
|
|
|
5527
5539
|
case "unary":
|
|
5528
5540
|
case "cast":
|
|
5529
5541
|
return explicitCollation(expr.expr);
|
|
5542
|
+
case "is_bool":
|
|
5543
|
+
return explicitCollation(expr.expr);
|
|
5530
5544
|
case "binary":
|
|
5531
5545
|
return explicitCollation(expr.left) ?? explicitCollation(expr.right);
|
|
5532
5546
|
case "between":
|
|
@@ -5557,6 +5571,8 @@ function inheritedCollation(expr, ctx) {
|
|
|
5557
5571
|
case "unary":
|
|
5558
5572
|
case "cast":
|
|
5559
5573
|
return inheritedCollation(expr.expr, ctx);
|
|
5574
|
+
case "is_bool":
|
|
5575
|
+
return inheritedCollation(expr.expr, ctx);
|
|
5560
5576
|
default:
|
|
5561
5577
|
return null;
|
|
5562
5578
|
}
|
|
@@ -5602,6 +5618,13 @@ function evalExpr(expr, ctx) {
|
|
|
5602
5618
|
if (expr.op === "-") return asNumber(-numberValue(value));
|
|
5603
5619
|
return ~integerValue(value);
|
|
5604
5620
|
}
|
|
5621
|
+
case "is_bool": {
|
|
5622
|
+
const truth = isTruthySql(evalExpr(expr.expr, ctx));
|
|
5623
|
+
if (!expr.not && expr.sense) return booleanValue(truth === true);
|
|
5624
|
+
if (!expr.not && !expr.sense) return booleanValue(truth === false);
|
|
5625
|
+
if (expr.not && expr.sense) return booleanValue(truth !== true);
|
|
5626
|
+
return booleanValue(truth !== false);
|
|
5627
|
+
}
|
|
5605
5628
|
case "binary":
|
|
5606
5629
|
return evalBinary(expr.op, expr.left, expr.right, ctx);
|
|
5607
5630
|
case "between": {
|
|
@@ -9038,8 +9061,13 @@ var ExecutionEnv = class {
|
|
|
9038
9061
|
if (table === null) return !cell.hiddenByUsing;
|
|
9039
9062
|
return (cell.tableLower ?? cell.table?.toLowerCase()) === tableKey;
|
|
9040
9063
|
});
|
|
9041
|
-
if (matches.length === 0)
|
|
9064
|
+
if (matches.length === 0) {
|
|
9065
|
+
if (table === null) {
|
|
9066
|
+
if (key === "true") return 1;
|
|
9067
|
+
if (key === "false") return 0;
|
|
9068
|
+
}
|
|
9042
9069
|
throw new SqliteError(`no such column: ${table ? `${table}.` : ""}${name}`, "no_such_column");
|
|
9070
|
+
}
|
|
9043
9071
|
if (matches.length > 1 && table === null) throw new SqliteError(`ambiguous column name: ${name}`, "other");
|
|
9044
9072
|
return matches[0].value;
|
|
9045
9073
|
},
|
|
@@ -9052,8 +9080,10 @@ var ExecutionEnv = class {
|
|
|
9052
9080
|
if (table === null) return !cell2.hiddenByUsing;
|
|
9053
9081
|
return (cell2.tableLower ?? cell2.table?.toLowerCase()) === tableKey;
|
|
9054
9082
|
});
|
|
9055
|
-
if (matches.length === 0)
|
|
9083
|
+
if (matches.length === 0) {
|
|
9084
|
+
if (table === null && (key === "true" || key === "false")) return "integer";
|
|
9056
9085
|
throw new SqliteError(`no such column: ${table ? `${table}.` : ""}${name}`, "no_such_column");
|
|
9086
|
+
}
|
|
9057
9087
|
if (matches.length > 1 && table === null) throw new SqliteError(`ambiguous column name: ${name}`, "other");
|
|
9058
9088
|
const cell = matches[0];
|
|
9059
9089
|
return cell.affinity === "REAL" && typeof cell.value === "number" ? "real" : storageClassOf(cell.value);
|
|
@@ -9981,6 +10011,8 @@ function exprEquals(left, right) {
|
|
|
9981
10011
|
return right.type === "column" && (left.table ?? "").toLowerCase() === (right.table ?? "").toLowerCase() && left.name.toLowerCase() === right.name.toLowerCase();
|
|
9982
10012
|
case "unary":
|
|
9983
10013
|
return right.type === "unary" && left.op === right.op && exprEquals(left.expr, right.expr);
|
|
10014
|
+
case "is_bool":
|
|
10015
|
+
return right.type === "is_bool" && left.not === right.not && left.sense === right.sense && exprEquals(left.expr, right.expr);
|
|
9984
10016
|
case "binary":
|
|
9985
10017
|
return right.type === "binary" && left.op === right.op && exprEquals(left.left, right.left) && exprEquals(left.right, right.right);
|
|
9986
10018
|
case "function":
|
|
@@ -11445,6 +11477,9 @@ function validateProjectedColumns(stmt, sample, env, parent) {
|
|
|
11445
11477
|
case "unary":
|
|
11446
11478
|
recurse(expr.expr);
|
|
11447
11479
|
break;
|
|
11480
|
+
case "is_bool":
|
|
11481
|
+
recurse(expr.expr);
|
|
11482
|
+
break;
|
|
11448
11483
|
case "binary":
|
|
11449
11484
|
recurse(expr.left);
|
|
11450
11485
|
recurse(expr.right);
|
|
@@ -11507,6 +11542,10 @@ function expressionName(expr) {
|
|
|
11507
11542
|
if (expr.type === "binary") {
|
|
11508
11543
|
return `${expressionName(expr.left)} ${expr.op} ${expressionName(expr.right)}`;
|
|
11509
11544
|
}
|
|
11545
|
+
if (expr.type === "is_bool") {
|
|
11546
|
+
const sense = expr.sense ? "TRUE" : "FALSE";
|
|
11547
|
+
return `${expressionName(expr.expr)} IS${expr.not ? " NOT" : ""} ${sense}`;
|
|
11548
|
+
}
|
|
11510
11549
|
return expr.type;
|
|
11511
11550
|
}
|
|
11512
11551
|
function replaceSpecial(expr, evaluate) {
|
|
@@ -11515,6 +11554,8 @@ function replaceSpecial(expr, evaluate) {
|
|
|
11515
11554
|
switch (expr.type) {
|
|
11516
11555
|
case "unary":
|
|
11517
11556
|
return { ...expr, expr: recurse(expr.expr) };
|
|
11557
|
+
case "is_bool":
|
|
11558
|
+
return { ...expr, expr: recurse(expr.expr) };
|
|
11518
11559
|
case "binary":
|
|
11519
11560
|
return { ...expr, left: recurse(expr.left), right: recurse(expr.right) };
|
|
11520
11561
|
case "between":
|