@minnowdb/core 0.2.1 → 0.4.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.
Files changed (270) hide show
  1. package/README.md +23 -32
  2. package/dist/block-format/block.d.ts +32 -13
  3. package/dist/block-format/block.js +179 -55
  4. package/dist/block-format/checksum.d.ts +2 -1
  5. package/dist/block-format/checksum.js +5 -2
  6. package/dist/block-format/codecs.d.ts +7 -2
  7. package/dist/block-format/codecs.js +53 -12
  8. package/dist/block-format/column.d.ts +3 -2
  9. package/dist/block-format/column.js +96 -30
  10. package/dist/block-format/index.d.ts +2 -2
  11. package/dist/block-format/index.js +2 -2
  12. package/dist/block-format/physical.d.ts +8 -2
  13. package/dist/block-format/physical.js +14 -6
  14. package/dist/block-format/types.d.ts +7 -3
  15. package/dist/block-format/types.js +0 -1
  16. package/dist/block-format/unicode.d.ts +11 -0
  17. package/dist/block-format/unicode.js +46 -0
  18. package/dist/date-value.d.ts +15 -0
  19. package/dist/date-value.js +64 -0
  20. package/dist/engine/artifact-cache.d.ts +2 -1
  21. package/dist/engine/artifact-cache.js +5 -1
  22. package/dist/engine/batch.d.ts +11 -3
  23. package/dist/engine/batch.js +17 -7
  24. package/dist/engine/buffered-writer.d.ts +2 -1
  25. package/dist/engine/buffered-writer.js +34 -9
  26. package/dist/engine/cache-limits.d.ts +25 -0
  27. package/dist/engine/cache-limits.js +25 -0
  28. package/dist/engine/catalog.d.ts +24 -5
  29. package/dist/engine/catalog.js +34 -7
  30. package/dist/engine/client.d.ts +29 -7
  31. package/dist/engine/client.js +216 -40
  32. package/dist/engine/database.d.ts +224 -39
  33. package/dist/engine/database.js +7475 -1539
  34. package/dist/engine/defaults.d.ts +4 -8
  35. package/dist/engine/defaults.js +46 -22
  36. package/dist/engine/errors.d.ts +19 -1
  37. package/dist/engine/errors.js +32 -2
  38. package/dist/engine/fts.d.ts +0 -6
  39. package/dist/engine/fts.js +41 -14
  40. package/dist/engine/group-index.d.ts +0 -1
  41. package/dist/engine/group-index.js +6 -11
  42. package/dist/engine/index.d.ts +16 -10
  43. package/dist/engine/index.js +12 -9
  44. package/dist/engine/join-index.d.ts +0 -1
  45. package/dist/engine/join-index.js +2 -2
  46. package/dist/engine/keyed-live.d.ts +57 -0
  47. package/dist/engine/keyed-live.js +226 -0
  48. package/dist/engine/live-api.d.ts +4 -0
  49. package/dist/engine/live-api.js +4 -0
  50. package/dist/engine/live.d.ts +43 -26
  51. package/dist/engine/live.js +504 -126
  52. package/dist/engine/memory.d.ts +2 -1
  53. package/dist/engine/memory.js +3 -2
  54. package/dist/engine/optimizer.d.ts +0 -1
  55. package/dist/engine/optimizer.js +262 -27
  56. package/dist/engine/query-api.d.ts +3 -0
  57. package/dist/engine/query-api.js +3 -0
  58. package/dist/engine/query-cache.d.ts +1 -2
  59. package/dist/engine/query-cache.js +7 -5
  60. package/dist/engine/query.d.ts +134 -29
  61. package/dist/engine/query.js +1668 -372
  62. package/dist/engine/result-wire.d.ts +0 -1
  63. package/dist/engine/result-wire.js +2 -2
  64. package/dist/engine/schema-wire.d.ts +6 -3
  65. package/dist/engine/schema-wire.js +40 -34
  66. package/dist/engine/schema.d.ts +156 -80
  67. package/dist/engine/schema.js +570 -108
  68. package/dist/engine/sort-keys.d.ts +4 -4
  69. package/dist/engine/sort-keys.js +44 -29
  70. package/dist/engine/sql-domains.d.ts +31 -0
  71. package/dist/engine/sql-domains.js +585 -0
  72. package/dist/engine/sql-driver.d.ts +19 -0
  73. package/dist/engine/sql-driver.js +1 -0
  74. package/dist/engine/sql-json.d.ts +7 -9
  75. package/dist/engine/sql-json.js +75 -15
  76. package/dist/engine/sql-semantics.d.ts +8 -3
  77. package/dist/engine/sql-semantics.js +458 -30
  78. package/dist/engine/typed-live.d.ts +67 -0
  79. package/dist/engine/typed-live.js +349 -0
  80. package/dist/engine/vector.d.ts +12 -2
  81. package/dist/engine/vector.js +635 -119
  82. package/dist/engine/worker-host.d.ts +9 -2
  83. package/dist/engine/worker-host.js +620 -152
  84. package/dist/engine/worker.d.ts +0 -1
  85. package/dist/engine/worker.js +3 -3
  86. package/dist/engine/write-block-planner.d.ts +18 -0
  87. package/dist/engine/write-block-planner.js +112 -0
  88. package/dist/index.d.ts +0 -1
  89. package/dist/index.js +0 -1
  90. package/dist/plan/index.d.ts +3 -4
  91. package/dist/plan/index.js +3 -4
  92. package/dist/storage/index.d.ts +1 -1
  93. package/dist/storage/index.js +1 -1
  94. package/dist/storage/indexeddb.d.ts +99 -51
  95. package/dist/storage/indexeddb.js +13448 -2660
  96. package/dist/storage/memory.d.ts +64 -57
  97. package/dist/storage/memory.js +1042 -128
  98. package/dist/storage/opfs/files.d.ts +18 -7
  99. package/dist/storage/opfs/files.js +115 -28
  100. package/dist/storage/opfs/index.d.ts +1 -1
  101. package/dist/storage/opfs/index.js +1 -1
  102. package/dist/storage/opfs/leader.d.ts +409 -75
  103. package/dist/storage/opfs/leader.js +4621 -650
  104. package/dist/storage/opfs/rpc.d.ts +15 -3
  105. package/dist/storage/opfs/rpc.js +224 -2
  106. package/dist/storage/opfs/snapshot-ledger.d.ts +40 -0
  107. package/dist/storage/opfs/snapshot-ledger.js +281 -0
  108. package/dist/storage/opfs/store.d.ts +33 -99
  109. package/dist/storage/opfs/store.js +453 -364
  110. package/dist/storage/persistence.d.ts +37 -0
  111. package/dist/storage/persistence.js +78 -0
  112. package/dist/storage/snapshot-stream.d.ts +24 -0
  113. package/dist/storage/snapshot-stream.js +897 -0
  114. package/dist/storage/snapshot.d.ts +9 -85
  115. package/dist/storage/snapshot.js +33 -265
  116. package/dist/storage/toolkit/extents.d.ts +41 -7
  117. package/dist/storage/toolkit/extents.js +402 -39
  118. package/dist/storage/toolkit/index.d.ts +4 -5
  119. package/dist/storage/toolkit/index.js +28 -4
  120. package/dist/storage/toolkit/record-core.d.ts +182 -55
  121. package/dist/storage/toolkit/record-core.js +6158 -1331
  122. package/dist/storage/toolkit/sync-file.d.ts +10 -1
  123. package/dist/storage/toolkit/sync-file.js +50 -2
  124. package/dist/storage/toolkit/wal.d.ts +23 -5
  125. package/dist/storage/toolkit/wal.js +89 -27
  126. package/dist/storage/toolkit/wire.d.ts +15 -2
  127. package/dist/storage/toolkit/wire.js +199 -10
  128. package/dist/storage/types.d.ts +1439 -204
  129. package/dist/storage/types.js +1745 -141
  130. package/dist/testing/block-store-conformance.d.ts +0 -1
  131. package/dist/testing/block-store-conformance.js +1009 -119
  132. package/dist/testing/index.d.ts +46 -26
  133. package/dist/testing/index.js +112 -57
  134. package/dist/testing/opfs-shim.d.ts +13 -3
  135. package/dist/testing/opfs-shim.js +40 -6
  136. package/dist/testing/simulator.d.ts +97 -0
  137. package/dist/testing/simulator.js +591 -0
  138. package/dist/testing/sqllogictest.d.ts +89 -0
  139. package/dist/testing/sqllogictest.js +434 -0
  140. package/dist/transactions/index.d.ts +92 -16
  141. package/dist/transactions/index.js +1074 -236
  142. package/dist/worker-protocol/index.d.ts +3 -2
  143. package/dist/worker-protocol/index.js +2 -5
  144. package/package.json +53 -3
  145. package/postgres-feature-profile.json +223 -0
  146. package/sql-feature-matrix.json +172 -252
  147. package/dist/block-format/block.d.ts.map +0 -1
  148. package/dist/block-format/block.js.map +0 -1
  149. package/dist/block-format/checksum.d.ts.map +0 -1
  150. package/dist/block-format/checksum.js.map +0 -1
  151. package/dist/block-format/codecs.d.ts.map +0 -1
  152. package/dist/block-format/codecs.js.map +0 -1
  153. package/dist/block-format/column.d.ts.map +0 -1
  154. package/dist/block-format/column.js.map +0 -1
  155. package/dist/block-format/index.d.ts.map +0 -1
  156. package/dist/block-format/index.js.map +0 -1
  157. package/dist/block-format/physical.d.ts.map +0 -1
  158. package/dist/block-format/physical.js.map +0 -1
  159. package/dist/block-format/types.d.ts.map +0 -1
  160. package/dist/block-format/types.js.map +0 -1
  161. package/dist/engine/artifact-cache.d.ts.map +0 -1
  162. package/dist/engine/artifact-cache.js.map +0 -1
  163. package/dist/engine/batch.d.ts.map +0 -1
  164. package/dist/engine/batch.js.map +0 -1
  165. package/dist/engine/buffered-writer.d.ts.map +0 -1
  166. package/dist/engine/buffered-writer.js.map +0 -1
  167. package/dist/engine/catalog.d.ts.map +0 -1
  168. package/dist/engine/catalog.js.map +0 -1
  169. package/dist/engine/client.d.ts.map +0 -1
  170. package/dist/engine/client.js.map +0 -1
  171. package/dist/engine/coordinator.d.ts +0 -17
  172. package/dist/engine/coordinator.d.ts.map +0 -1
  173. package/dist/engine/coordinator.js +0 -60
  174. package/dist/engine/coordinator.js.map +0 -1
  175. package/dist/engine/database.d.ts.map +0 -1
  176. package/dist/engine/database.js.map +0 -1
  177. package/dist/engine/defaults.d.ts.map +0 -1
  178. package/dist/engine/defaults.js.map +0 -1
  179. package/dist/engine/errors.d.ts.map +0 -1
  180. package/dist/engine/errors.js.map +0 -1
  181. package/dist/engine/fts.d.ts.map +0 -1
  182. package/dist/engine/fts.js.map +0 -1
  183. package/dist/engine/group-index.d.ts.map +0 -1
  184. package/dist/engine/group-index.js.map +0 -1
  185. package/dist/engine/index.d.ts.map +0 -1
  186. package/dist/engine/index.js.map +0 -1
  187. package/dist/engine/join-index.d.ts.map +0 -1
  188. package/dist/engine/join-index.js.map +0 -1
  189. package/dist/engine/live.d.ts.map +0 -1
  190. package/dist/engine/live.js.map +0 -1
  191. package/dist/engine/memory.d.ts.map +0 -1
  192. package/dist/engine/memory.js.map +0 -1
  193. package/dist/engine/optimizer.d.ts.map +0 -1
  194. package/dist/engine/optimizer.js.map +0 -1
  195. package/dist/engine/query-cache.d.ts.map +0 -1
  196. package/dist/engine/query-cache.js.map +0 -1
  197. package/dist/engine/query.d.ts.map +0 -1
  198. package/dist/engine/query.js.map +0 -1
  199. package/dist/engine/result-wire.d.ts.map +0 -1
  200. package/dist/engine/result-wire.js.map +0 -1
  201. package/dist/engine/schema-wire.d.ts.map +0 -1
  202. package/dist/engine/schema-wire.js.map +0 -1
  203. package/dist/engine/schema.d.ts.map +0 -1
  204. package/dist/engine/schema.js.map +0 -1
  205. package/dist/engine/sort-keys.d.ts.map +0 -1
  206. package/dist/engine/sort-keys.js.map +0 -1
  207. package/dist/engine/sql-json.d.ts.map +0 -1
  208. package/dist/engine/sql-json.js.map +0 -1
  209. package/dist/engine/sql-semantics.d.ts.map +0 -1
  210. package/dist/engine/sql-semantics.js.map +0 -1
  211. package/dist/engine/vector.d.ts.map +0 -1
  212. package/dist/engine/vector.js.map +0 -1
  213. package/dist/engine/worker-host.d.ts.map +0 -1
  214. package/dist/engine/worker-host.js.map +0 -1
  215. package/dist/engine/worker.d.ts.map +0 -1
  216. package/dist/engine/worker.js.map +0 -1
  217. package/dist/index.d.ts.map +0 -1
  218. package/dist/index.js.map +0 -1
  219. package/dist/plan/index.d.ts.map +0 -1
  220. package/dist/plan/index.js.map +0 -1
  221. package/dist/storage/fixture-shape.d.ts +0 -42
  222. package/dist/storage/fixture-shape.d.ts.map +0 -1
  223. package/dist/storage/fixture-shape.js +0 -146
  224. package/dist/storage/fixture-shape.js.map +0 -1
  225. package/dist/storage/index.d.ts.map +0 -1
  226. package/dist/storage/index.js.map +0 -1
  227. package/dist/storage/indexeddb.d.ts.map +0 -1
  228. package/dist/storage/indexeddb.js.map +0 -1
  229. package/dist/storage/memory.d.ts.map +0 -1
  230. package/dist/storage/memory.js.map +0 -1
  231. package/dist/storage/opfs/files.d.ts.map +0 -1
  232. package/dist/storage/opfs/files.js.map +0 -1
  233. package/dist/storage/opfs/index.d.ts.map +0 -1
  234. package/dist/storage/opfs/index.js.map +0 -1
  235. package/dist/storage/opfs/leader.d.ts.map +0 -1
  236. package/dist/storage/opfs/leader.js.map +0 -1
  237. package/dist/storage/opfs/rpc.d.ts.map +0 -1
  238. package/dist/storage/opfs/rpc.js.map +0 -1
  239. package/dist/storage/opfs/store.d.ts.map +0 -1
  240. package/dist/storage/opfs/store.js.map +0 -1
  241. package/dist/storage/snapshot.d.ts.map +0 -1
  242. package/dist/storage/snapshot.js.map +0 -1
  243. package/dist/storage/toolkit/extents.d.ts.map +0 -1
  244. package/dist/storage/toolkit/extents.js.map +0 -1
  245. package/dist/storage/toolkit/index.d.ts.map +0 -1
  246. package/dist/storage/toolkit/index.js.map +0 -1
  247. package/dist/storage/toolkit/record-core.d.ts.map +0 -1
  248. package/dist/storage/toolkit/record-core.js.map +0 -1
  249. package/dist/storage/toolkit/sync-file.d.ts.map +0 -1
  250. package/dist/storage/toolkit/sync-file.js.map +0 -1
  251. package/dist/storage/toolkit/wal.d.ts.map +0 -1
  252. package/dist/storage/toolkit/wal.js.map +0 -1
  253. package/dist/storage/toolkit/wire.d.ts.map +0 -1
  254. package/dist/storage/toolkit/wire.js.map +0 -1
  255. package/dist/storage/types.d.ts.map +0 -1
  256. package/dist/storage/types.js.map +0 -1
  257. package/dist/testing/block-store-conformance.d.ts.map +0 -1
  258. package/dist/testing/block-store-conformance.js.map +0 -1
  259. package/dist/testing/index.d.ts.map +0 -1
  260. package/dist/testing/index.js.map +0 -1
  261. package/dist/testing/opfs-shim.d.ts.map +0 -1
  262. package/dist/testing/opfs-shim.js.map +0 -1
  263. package/dist/testing/seeds.d.ts +0 -11
  264. package/dist/testing/seeds.d.ts.map +0 -1
  265. package/dist/testing/seeds.js +0 -50
  266. package/dist/testing/seeds.js.map +0 -1
  267. package/dist/transactions/index.d.ts.map +0 -1
  268. package/dist/transactions/index.js.map +0 -1
  269. package/dist/worker-protocol/index.d.ts.map +0 -1
  270. package/dist/worker-protocol/index.js.map +0 -1
@@ -3,6 +3,8 @@ export interface QueryMemoryUsage {
3
3
  readonly usedBytes: number;
4
4
  readonly peakBytes: number;
5
5
  }
6
+ /** Finite default for every public engine execution path. */
7
+ export declare const DEFAULT_QUERY_MEMORY_BUDGET_BYTES: number;
6
8
  export declare class QueryMemoryBudgetError extends Error {
7
9
  readonly label: string;
8
10
  readonly requestedBytes: number;
@@ -34,4 +36,3 @@ export declare class QueryMemoryReservation {
34
36
  constructor(context: QueryMemoryContext, bytes: number);
35
37
  release(): void;
36
38
  }
37
- //# sourceMappingURL=memory.d.ts.map
@@ -1,3 +1,5 @@
1
+ /** Finite default for every public engine execution path. */
2
+ export const DEFAULT_QUERY_MEMORY_BUDGET_BYTES = 64 * 1024 * 1024;
1
3
  export class QueryMemoryBudgetError extends Error {
2
4
  label;
3
5
  requestedBytes;
@@ -19,7 +21,7 @@ export class QueryMemoryContext {
19
21
  #reservations = new Set();
20
22
  #talliedBytes = 0;
21
23
  #closed = false;
22
- constructor(budgetBytes = Number.MAX_SAFE_INTEGER, parent) {
24
+ constructor(budgetBytes = DEFAULT_QUERY_MEMORY_BUDGET_BYTES, parent) {
23
25
  if (!Number.isSafeInteger(budgetBytes) || budgetBytes < 0) {
24
26
  throw new RangeError("Query memory budget must be a non-negative safe integer");
25
27
  }
@@ -116,4 +118,3 @@ function validateMemoryBytes(bytes, label) {
116
118
  throw new RangeError(`${label} must be a non-negative safe integer`);
117
119
  }
118
120
  }
119
- //# sourceMappingURL=memory.js.map
@@ -17,4 +17,3 @@ export declare function chooseJoinOrder(plan: CompiledQuery, inputs: ReadonlyMap
17
17
  }>): CompiledQuery;
18
18
  /** Renders a stable, human-readable plan for snapshot tests and explain(). */
19
19
  export declare function renderPlan(plan: CompiledQuery): string;
20
- //# sourceMappingURL=optimizer.d.ts.map
@@ -1,4 +1,6 @@
1
- import { childExpressions, expressionAliases, forEachBlockExpression, forEachNestedBlock, isScalarFunctionName, scalarFunctionNames, scalarFunctionValue, splitCondition, } from "./query.js";
1
+ import { dateIsoString, dateMilliseconds } from "../date-value.js";
2
+ import { childExpressions, expressionAliases, forEachBlockExpression, forEachNestedBlock, isScalarFunctionName, scalarFunctionNames, scalarFunctionValue, splitCondition, volatileScalarFunctionNames, } from "./query.js";
3
+ import { isSqlDomainValue } from "./sql-domains.js";
2
4
  /**
3
5
  * Deterministic plan-to-plan rewrites over the shared compiled representation. Every rule
4
6
  * preserves result semantics exactly; rules that cannot prove safety leave the plan unchanged.
@@ -37,6 +39,7 @@ export function chooseJoinOrder(plan, inputs) {
37
39
  };
38
40
  }
39
41
  function optimizeBlock(block) {
42
+ decorrelateLateralSources(block);
40
43
  decorrelateBlock(block);
41
44
  for (const source of [block.base, ...block.joins]) {
42
45
  if (source.derived !== undefined)
@@ -57,6 +60,79 @@ function optimizeBlock(block) {
57
60
  pruneDerivedProjections(block);
58
61
  combineDerivedLimit(block);
59
62
  }
63
+ /** Converts equality-correlated LATERAL derived tables into ordinary set-at-a-time joins. */
64
+ function decorrelateLateralSources(block) {
65
+ if (block.base.lateral === true) {
66
+ throw new TypeError("LATERAL needs a source to its left");
67
+ }
68
+ const available = new Set([block.base.alias]);
69
+ for (const join of block.joins) {
70
+ if (join.lateral !== true) {
71
+ available.add(join.alias);
72
+ continue;
73
+ }
74
+ delete join.lateral;
75
+ const inner = join.derived;
76
+ if (inner === undefined)
77
+ throw new TypeError("LATERAL requires a derived query");
78
+ if (!blockReferencesOutside(inner)) {
79
+ available.add(join.alias);
80
+ continue;
81
+ }
82
+ if (inner.groupBy.length > 0 ||
83
+ inner.having.length > 0 ||
84
+ inner.orderBy.length > 0 ||
85
+ inner.limit !== undefined) {
86
+ throw new TypeError("Correlated LATERAL queries cannot use grouping, HAVING, ORDER BY, or LIMIT");
87
+ }
88
+ const keys = extractCorrelation(inner, available, "LATERAL");
89
+ const keyAliases = keys.map((_, index) => `\u0000lateral_key_${String(index + 1)}`);
90
+ keys.forEach((key, index) => {
91
+ inner.select.push({ expression: key.inner, alias: keyAliases[index] ?? "" });
92
+ });
93
+ const correlations = keys.map((key, index) => ({
94
+ kind: "condition",
95
+ operator: key.operator,
96
+ left: key.outer,
97
+ right: { kind: "column", reference: `${join.alias}.${keyAliases[index] ?? ""}` },
98
+ }));
99
+ const cross = join.on?.kind === "condition" &&
100
+ join.on.operator === "=" &&
101
+ join.on.left.kind === "literal" &&
102
+ join.on.left.value === 1 &&
103
+ join.on.right.kind === "literal" &&
104
+ join.on.right.value === 1;
105
+ const original = cross
106
+ ? undefined
107
+ : (join.on ?? {
108
+ kind: "condition",
109
+ operator: "=",
110
+ left: join.left,
111
+ right: join.right,
112
+ });
113
+ const conditions = [...correlations, ...(original === undefined ? [] : [original])];
114
+ const first = conditions[0];
115
+ if (conditions.length === 1 && first?.kind === "condition" && first.operator === "=") {
116
+ join.left = first.left;
117
+ join.right = first.right;
118
+ delete join.on;
119
+ }
120
+ else {
121
+ join.left = { kind: "literal", value: null };
122
+ join.right = { kind: "literal", value: null };
123
+ const seed = conditions[0];
124
+ if (seed === undefined)
125
+ throw new Error("LATERAL correlation condition is missing");
126
+ join.on = conditions.slice(1).reduce((combined, condition) => ({
127
+ kind: "logical",
128
+ operator: "and",
129
+ left: combined,
130
+ right: condition,
131
+ }), seed);
132
+ }
133
+ available.add(join.alias);
134
+ }
135
+ }
60
136
  // --- Join key extraction ---------------------------------------------------------------------
61
137
  /**
62
138
  * Pulls a hash key out of a conjunctive ON clause.
@@ -391,7 +467,7 @@ function decorrelatePredicate(block, predicate, scope, nextAlias) {
391
467
  return undefined;
392
468
  rejectGroupedInner(inner, "EXISTS");
393
469
  guardWildcard(block);
394
- const keys = extractCorrelation(inner, scope, "EXISTS");
470
+ const keys = extractCorrelation(inner, scope, "EXISTS", true);
395
471
  const alias = nextAlias();
396
472
  const derived = {
397
473
  sql: "(correlated exists)",
@@ -405,7 +481,10 @@ function decorrelatePredicate(block, predicate, scope, nextAlias) {
405
481
  having: [],
406
482
  orderBy: [],
407
483
  };
408
- pushCorrelationJoin(block, alias, derived, keys, negated ? "left" : "inner");
484
+ const general = keys.some((key) => key.operator !== "=");
485
+ pushCorrelationJoin(block, alias, derived, keys, general ? (negated ? "anti" : "semi") : negated ? "left" : "inner");
486
+ if (general)
487
+ return "consumed";
409
488
  if (!negated)
410
489
  return "consumed";
411
490
  return {
@@ -414,15 +493,12 @@ function decorrelatePredicate(block, predicate, scope, nextAlias) {
414
493
  right: { kind: "literal", value: null },
415
494
  };
416
495
  }
417
- // outer IN (correlated subquery)
496
+ // outer IN / NOT IN (correlated subquery)
418
497
  if ((predicate.operator === "IN" || predicate.operator === "NOT IN") &&
419
498
  predicate.right.kind === "subquery") {
420
499
  const inner = predicate.right.block;
421
500
  if (!blockReferencesOutside(inner))
422
501
  return undefined;
423
- if (predicate.operator === "NOT IN") {
424
- throw new TypeError("Correlated NOT IN subqueries are not supported; use NOT EXISTS");
425
- }
426
502
  rejectGroupedInner(inner, "IN");
427
503
  const item = inner.select[0];
428
504
  if (inner.select.length !== 1 || item === undefined || item.expression.kind === "wildcard") {
@@ -430,6 +506,9 @@ function decorrelatePredicate(block, predicate, scope, nextAlias) {
430
506
  }
431
507
  guardWildcard(block);
432
508
  const keys = extractCorrelation(inner, scope, "IN");
509
+ if (predicate.operator === "NOT IN") {
510
+ return decorrelateNotIn(block, predicate.left, inner, item.expression, keys, nextAlias);
511
+ }
433
512
  const alias = nextAlias();
434
513
  const derived = {
435
514
  sql: "(correlated in)",
@@ -469,6 +548,106 @@ function decorrelatePredicate(block, predicate, scope, nextAlias) {
469
548
  ? { left: value, operator: predicate.operator, right: predicate.right }
470
549
  : { left: predicate.left, operator: predicate.operator, right: value };
471
550
  }
551
+ /**
552
+ * Rewrites `probe NOT IN (correlated values)` without losing SQL's empty-set and NULL rules.
553
+ *
554
+ * One hash anti-join removes exact matches using a prefix-free composite key containing every
555
+ * correlation key plus the compared value. A grouped left join then carries two counts for the
556
+ * correlated set: total rows and non-null values. An absent group means the set is empty (true,
557
+ * even for a NULL probe); otherwise the probe must be non-null and the two counts equal. The two
558
+ * derived scans are bounded set-at-a-time work—no per-outer-row subquery or nested-loop join.
559
+ */
560
+ function decorrelateNotIn(block, probe, inner, item, keys, nextAlias) {
561
+ const valuesAlias = nextAlias();
562
+ const values = {
563
+ sql: "(correlated not in values)",
564
+ base: structuredClone(inner.base),
565
+ joins: structuredClone(inner.joins),
566
+ select: [
567
+ ...keys.map((key, index) => ({
568
+ expression: structuredClone(key.inner),
569
+ alias: `k${String(index)}`,
570
+ })),
571
+ { expression: structuredClone(item), alias: "v" },
572
+ ],
573
+ predicates: structuredClone(inner.predicates),
574
+ groupBy: [...keys.map((key) => structuredClone(key.inner)), structuredClone(item)],
575
+ having: [],
576
+ orderBy: [],
577
+ };
578
+ const tuple = (arguments_) => ({
579
+ kind: "call",
580
+ name: "MINNOW_TUPLE_KEY",
581
+ arguments: arguments_,
582
+ });
583
+ block.joins.push({
584
+ table: valuesAlias,
585
+ alias: valuesAlias,
586
+ derived: values,
587
+ kind: "anti",
588
+ left: tuple([...keys.map((key) => structuredClone(key.outer)), structuredClone(probe)]),
589
+ right: tuple([
590
+ ...keys.map((_, index) => ({
591
+ kind: "column",
592
+ reference: `${valuesAlias}.k${String(index)}`,
593
+ })),
594
+ { kind: "column", reference: `${valuesAlias}.v` },
595
+ ]),
596
+ });
597
+ const flagsAlias = nextAlias();
598
+ const flags = {
599
+ sql: "(correlated not in flags)",
600
+ base: structuredClone(inner.base),
601
+ joins: structuredClone(inner.joins),
602
+ select: [
603
+ ...keys.map((key, index) => ({
604
+ expression: structuredClone(key.inner),
605
+ alias: `k${String(index)}`,
606
+ })),
607
+ {
608
+ expression: { kind: "call", name: "COUNT", arguments: [{ kind: "wildcard" }] },
609
+ alias: "n",
610
+ },
611
+ {
612
+ expression: { kind: "call", name: "COUNT", arguments: [structuredClone(item)] },
613
+ alias: "nn",
614
+ },
615
+ ],
616
+ predicates: structuredClone(inner.predicates),
617
+ groupBy: keys.map((key) => structuredClone(key.inner)),
618
+ having: [],
619
+ orderBy: [],
620
+ };
621
+ pushCorrelationJoin(block, flagsAlias, flags, keys, "left");
622
+ const total = { kind: "column", reference: `${flagsAlias}.n` };
623
+ const nonNull = { kind: "column", reference: `${flagsAlias}.nn` };
624
+ const safe = {
625
+ kind: "logical",
626
+ operator: "or",
627
+ left: {
628
+ kind: "condition",
629
+ operator: "IS NULL",
630
+ left: total,
631
+ right: { kind: "literal", value: null },
632
+ },
633
+ right: {
634
+ kind: "logical",
635
+ operator: "and",
636
+ left: {
637
+ kind: "condition",
638
+ operator: "IS NOT NULL",
639
+ left: structuredClone(probe),
640
+ right: { kind: "literal", value: null },
641
+ },
642
+ right: { kind: "condition", operator: "=", left: total, right: nonNull },
643
+ },
644
+ };
645
+ return {
646
+ left: safe,
647
+ operator: "IS TRUE",
648
+ right: { kind: "literal", value: true },
649
+ };
650
+ }
472
651
  /**
473
652
  * Rewrites one correlated scalar-aggregate subquery into a left join against the grouped
474
653
  * aggregate, returning the expression that replaces the subquery node (COUNT wraps in COALESCE
@@ -518,20 +697,22 @@ function pushCorrelationJoin(block, alias, derived, keys, kind) {
518
697
  reference: `${alias}.k${String(index)}`,
519
698
  });
520
699
  const first = keys[0];
521
- if (keys.length === 1 && first !== undefined) {
700
+ if (keys.length === 1 && first?.operator === "=") {
522
701
  block.joins.push({ ...source, kind, left: first.outer, right: keyReference(0) });
523
702
  return;
524
703
  }
525
704
  let on;
526
705
  keys.forEach((key, index) => {
527
- const equality = {
706
+ const comparison = {
528
707
  kind: "condition",
529
- operator: "=",
530
- left: key.outer,
531
- right: keyReference(index),
708
+ operator: key.operator,
709
+ left: keyReference(index),
710
+ right: key.outer,
532
711
  };
533
712
  on =
534
- on === undefined ? equality : { kind: "logical", operator: "and", left: on, right: equality };
713
+ on === undefined
714
+ ? comparison
715
+ : { kind: "logical", operator: "and", left: on, right: comparison };
535
716
  });
536
717
  block.joins.push({
537
718
  ...source,
@@ -570,12 +751,12 @@ function collectOutsideReferences(block, outer, refs) {
570
751
  collectOutsideReferences(nested, scope, refs);
571
752
  });
572
753
  }
573
- function extractCorrelation(inner, outerScope, label) {
754
+ function extractCorrelation(inner, outerScope, label, comparisons = false) {
574
755
  const innerScope = new Set([inner.base.alias, ...inner.joins.map((join) => join.alias)]);
575
756
  const keys = [];
576
757
  const kept = [];
577
758
  for (const predicate of inner.predicates) {
578
- const key = correlationEquality(predicate, innerScope, outerScope);
759
+ const key = correlationComparison(predicate, innerScope, outerScope, comparisons);
579
760
  if (key === undefined)
580
761
  kept.push(predicate);
581
762
  else
@@ -592,9 +773,12 @@ function extractCorrelation(inner, outerScope, label) {
592
773
  }
593
774
  return keys;
594
775
  }
595
- function correlationEquality(predicate, innerScope, outerScope) {
596
- if (predicate.operator !== "=")
776
+ function correlationComparison(predicate, innerScope, outerScope, comparisons) {
777
+ if (predicate.operator !== "=" &&
778
+ (!comparisons || !comparisonOperators.has(predicate.operator))) {
597
779
  return undefined;
780
+ }
781
+ const operator = predicate.operator;
598
782
  const { left, right } = predicate;
599
783
  if (left.kind !== "column" || right.kind !== "column")
600
784
  return undefined;
@@ -613,11 +797,26 @@ function correlationEquality(predicate, innerScope, outerScope) {
613
797
  const leftSide = sideOf(left.reference);
614
798
  const rightSide = sideOf(right.reference);
615
799
  if (leftSide === "inner" && rightSide === "outer")
616
- return { inner: left, outer: right };
617
- if (leftSide === "outer" && rightSide === "inner")
618
- return { inner: right, outer: left };
800
+ return { inner: left, outer: right, operator };
801
+ if (leftSide === "outer" && rightSide === "inner") {
802
+ return { inner: right, outer: left, operator: reverseComparison(operator) };
803
+ }
619
804
  return undefined;
620
805
  }
806
+ function reverseComparison(operator) {
807
+ switch (operator) {
808
+ case ">":
809
+ return "<";
810
+ case ">=":
811
+ return "<=";
812
+ case "<":
813
+ return ">";
814
+ case "<=":
815
+ return ">=";
816
+ default:
817
+ return operator;
818
+ }
819
+ }
621
820
  function rejectGroupedInner(inner, label) {
622
821
  if (inner.groupBy.length > 0 || inner.having.length > 0) {
623
822
  throw new TypeError(`Correlated ${label} subqueries cannot use GROUP BY or HAVING`);
@@ -661,8 +860,17 @@ function foldExpression(expression) {
661
860
  const right = foldExpression(expression.right);
662
861
  if (left.kind === "literal" && right.kind === "literal") {
663
862
  const folded = foldBinary(expression.operator, left.value, right.value);
664
- if (folded !== undefined)
665
- return { kind: "literal", value: folded };
863
+ if (folded !== undefined) {
864
+ const domain = left.sqlDomain?.kind === "numeric" || right.sqlDomain?.kind === "numeric"
865
+ ? { kind: "numeric" }
866
+ : undefined;
867
+ return {
868
+ kind: "literal",
869
+ value: folded,
870
+ ...(isSqlDomainValue(folded) ? { internalSqlValue: true } : {}),
871
+ ...(domain === undefined ? {} : { sqlDomain: domain }),
872
+ };
873
+ }
666
874
  }
667
875
  return { ...expression, left, right };
668
876
  }
@@ -670,6 +878,7 @@ function foldExpression(expression) {
670
878
  const foldedArguments = expression.arguments.map(foldExpression);
671
879
  const literalValues = foldedArguments.flatMap((argument) => argument.kind === "literal" ? [argument.value] : []);
672
880
  if (expression.name !== "COALESCE" &&
881
+ !volatileScalarFunctionNames.has(expression.name) &&
673
882
  isScalarFunctionName(expression.name) &&
674
883
  literalValues.length === foldedArguments.length) {
675
884
  try {
@@ -679,7 +888,34 @@ function foldExpression(expression) {
679
888
  typeof folded === "boolean" ||
680
889
  folded instanceof Date ||
681
890
  (typeof folded === "number" && Number.isFinite(folded))) {
682
- return { kind: "literal", value: folded };
891
+ const target = foldedArguments[1];
892
+ const targetWord = expression.name === "CAST" &&
893
+ target?.kind === "literal" &&
894
+ typeof target.value === "string"
895
+ ? target.value
896
+ : undefined;
897
+ const sqlDomain = targetWord?.startsWith("numeric:") === true
898
+ ? (() => {
899
+ const [, precisionWord = "", scaleWord = ""] = targetWord.split(":");
900
+ return {
901
+ kind: "numeric",
902
+ ...(precisionWord === "" ? {} : { precision: Number(precisionWord) }),
903
+ ...(scaleWord === "" ? {} : { scale: Number(scaleWord) }),
904
+ };
905
+ })()
906
+ : targetWord === "json" ||
907
+ targetWord === "jsonb" ||
908
+ targetWord === "uuid" ||
909
+ targetWord === "time" ||
910
+ targetWord === "interval"
911
+ ? { kind: targetWord }
912
+ : undefined;
913
+ return {
914
+ kind: "literal",
915
+ value: folded,
916
+ ...(isSqlDomainValue(folded) ? { internalSqlValue: true } : {}),
917
+ ...(sqlDomain === undefined ? {} : { sqlDomain }),
918
+ };
683
919
  }
684
920
  }
685
921
  catch {
@@ -741,8 +977,8 @@ function foldBinary(operator, leftValue, rightValue) {
741
977
  return undefined;
742
978
  if (typeof leftValue === "string" || typeof rightValue === "string")
743
979
  return undefined;
744
- const left = leftValue instanceof Date ? leftValue.getTime() : leftValue;
745
- const right = rightValue instanceof Date ? rightValue.getTime() : rightValue;
980
+ const left = leftValue instanceof Date ? dateMilliseconds(leftValue) : leftValue;
981
+ const right = rightValue instanceof Date ? dateMilliseconds(rightValue) : rightValue;
746
982
  if ((operator === "/" || operator === "%") && right === 0)
747
983
  return null;
748
984
  const result = operator === "+"
@@ -1125,7 +1361,7 @@ function renderExpression(expression) {
1125
1361
  if (value === null)
1126
1362
  return "null";
1127
1363
  if (value instanceof Date)
1128
- return `date ${value.toISOString()}`;
1364
+ return `date ${dateIsoString(value)}`;
1129
1365
  if (typeof value === "string")
1130
1366
  return `'${value}'`;
1131
1367
  return String(value);
@@ -1167,4 +1403,3 @@ function renderExpression(expression) {
1167
1403
  }
1168
1404
  return `${expression.name}(...) over (...)`;
1169
1405
  }
1170
- //# sourceMappingURL=optimizer.js.map
@@ -0,0 +1,3 @@
1
+ /** SQL compilation, standalone execution, and plan inspection for tooling and adapters. */
2
+ export { bindPlanParameters, bindStatementParameters, compileQuery, compileStatement, executeQuery, referencedColumns, type InsertValue, type CompiledQuery, type CompiledStatement, type QueryResult, type QueryRow, type QueryExecutionOptions, type QueryValue, type SqlColumnSchema, type SqlColumnType, } from "./query.js";
3
+ export { optimizePlan, renderPlan } from "./optimizer.js";
@@ -0,0 +1,3 @@
1
+ /** SQL compilation, standalone execution, and plan inspection for tooling and adapters. */
2
+ export { bindPlanParameters, bindStatementParameters, compileQuery, compileStatement, executeQuery, referencedColumns, } from "./query.js";
3
+ export { optimizePlan, renderPlan } from "./optimizer.js";
@@ -1,4 +1,4 @@
1
- import type { QueryResult } from "./query.js";
1
+ import { type QueryResult } from "./query.js";
2
2
  /** Modest per-entry cap so one giant result cannot thrash the shared artifact cache. */
3
3
  export declare const RESULT_MEMO_MAX_BYTES: number;
4
4
  /** Stable, collision-free memo-key encoding for bound SQL parameters. */
@@ -19,4 +19,3 @@ export declare function planMemoKey(plan: unknown): string;
19
19
  export declare function copyQueryResult(result: QueryResult): QueryResult;
20
20
  /** Modeled retained payload for one cached query result. */
21
21
  export declare function queryResultRetainedBytes(result: QueryResult): number;
22
- //# sourceMappingURL=query-cache.d.ts.map
@@ -1,3 +1,5 @@
1
+ import { copyDate, dateMilliseconds } from "../date-value.js";
2
+ import { copyQueryResultExternalization } from "./query.js";
1
3
  import { defineSqlResultProperty } from "./sql-semantics.js";
2
4
  /** Modest per-entry cap so one giant result cannot thrash the shared artifact cache. */
3
5
  export const RESULT_MEMO_MAX_BYTES = 4 * 1024 * 1024;
@@ -13,7 +15,7 @@ export function queryResultMemoKey(sql, params) {
13
15
  export function planMemoKey(plan) {
14
16
  return JSON.stringify(plan, (_key, value) => {
15
17
  if (value instanceof Date)
16
- return { $date: value.getTime() };
18
+ return { $date: dateMilliseconds(value) };
17
19
  if (typeof value === "bigint")
18
20
  return { $bigint: value.toString() };
19
21
  if (typeof value === "number") {
@@ -34,18 +36,19 @@ export function planMemoKey(plan) {
34
36
  */
35
37
  export function copyQueryResult(result) {
36
38
  const columns = result.columns;
37
- return {
39
+ const copy = {
38
40
  columns: [...columns],
39
41
  rows: result.rows.map((row) => {
40
42
  const copy = { ...row };
41
43
  for (const name of columns) {
42
44
  const value = copy[name];
43
45
  if (value instanceof Date)
44
- defineSqlResultProperty(copy, name, new Date(value.getTime()));
46
+ defineSqlResultProperty(copy, name, copyDate(value));
45
47
  }
46
48
  return copy;
47
49
  }),
48
50
  };
51
+ return copyQueryResultExternalization(result, copy);
49
52
  }
50
53
  /** Modeled retained payload for one cached query result. */
51
54
  export function queryResultRetainedBytes(result) {
@@ -81,7 +84,6 @@ function encodeParameter(value) {
81
84
  if (typeof value === "string")
82
85
  return [3, value];
83
86
  if (value instanceof Date)
84
- return [4, value.getTime()];
87
+ return [4, dateMilliseconds(value)];
85
88
  throw new TypeError("Query parameter has an unsupported value");
86
89
  }
87
- //# sourceMappingURL=query-cache.js.map