@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 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.
@@ -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":