@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.
- package/README.md +23 -32
- package/dist/block-format/block.d.ts +32 -13
- package/dist/block-format/block.js +179 -55
- package/dist/block-format/checksum.d.ts +2 -1
- package/dist/block-format/checksum.js +5 -2
- package/dist/block-format/codecs.d.ts +7 -2
- package/dist/block-format/codecs.js +53 -12
- package/dist/block-format/column.d.ts +3 -2
- package/dist/block-format/column.js +96 -30
- package/dist/block-format/index.d.ts +2 -2
- package/dist/block-format/index.js +2 -2
- package/dist/block-format/physical.d.ts +8 -2
- package/dist/block-format/physical.js +14 -6
- package/dist/block-format/types.d.ts +7 -3
- package/dist/block-format/types.js +0 -1
- package/dist/block-format/unicode.d.ts +11 -0
- package/dist/block-format/unicode.js +46 -0
- package/dist/date-value.d.ts +15 -0
- package/dist/date-value.js +64 -0
- package/dist/engine/artifact-cache.d.ts +2 -1
- package/dist/engine/artifact-cache.js +5 -1
- package/dist/engine/batch.d.ts +11 -3
- package/dist/engine/batch.js +17 -7
- package/dist/engine/buffered-writer.d.ts +2 -1
- package/dist/engine/buffered-writer.js +34 -9
- package/dist/engine/cache-limits.d.ts +25 -0
- package/dist/engine/cache-limits.js +25 -0
- package/dist/engine/catalog.d.ts +24 -5
- package/dist/engine/catalog.js +34 -7
- package/dist/engine/client.d.ts +29 -7
- package/dist/engine/client.js +216 -40
- package/dist/engine/database.d.ts +224 -39
- package/dist/engine/database.js +7475 -1539
- package/dist/engine/defaults.d.ts +4 -8
- package/dist/engine/defaults.js +46 -22
- package/dist/engine/errors.d.ts +19 -1
- package/dist/engine/errors.js +32 -2
- package/dist/engine/fts.d.ts +0 -6
- package/dist/engine/fts.js +41 -14
- package/dist/engine/group-index.d.ts +0 -1
- package/dist/engine/group-index.js +6 -11
- package/dist/engine/index.d.ts +16 -10
- package/dist/engine/index.js +12 -9
- package/dist/engine/join-index.d.ts +0 -1
- package/dist/engine/join-index.js +2 -2
- package/dist/engine/keyed-live.d.ts +57 -0
- package/dist/engine/keyed-live.js +226 -0
- package/dist/engine/live-api.d.ts +4 -0
- package/dist/engine/live-api.js +4 -0
- package/dist/engine/live.d.ts +43 -26
- package/dist/engine/live.js +504 -126
- package/dist/engine/memory.d.ts +2 -1
- package/dist/engine/memory.js +3 -2
- package/dist/engine/optimizer.d.ts +0 -1
- package/dist/engine/optimizer.js +262 -27
- package/dist/engine/query-api.d.ts +3 -0
- package/dist/engine/query-api.js +3 -0
- package/dist/engine/query-cache.d.ts +1 -2
- package/dist/engine/query-cache.js +7 -5
- package/dist/engine/query.d.ts +134 -29
- package/dist/engine/query.js +1668 -372
- package/dist/engine/result-wire.d.ts +0 -1
- package/dist/engine/result-wire.js +2 -2
- package/dist/engine/schema-wire.d.ts +6 -3
- package/dist/engine/schema-wire.js +40 -34
- package/dist/engine/schema.d.ts +156 -80
- package/dist/engine/schema.js +570 -108
- package/dist/engine/sort-keys.d.ts +4 -4
- package/dist/engine/sort-keys.js +44 -29
- package/dist/engine/sql-domains.d.ts +31 -0
- package/dist/engine/sql-domains.js +585 -0
- package/dist/engine/sql-driver.d.ts +19 -0
- package/dist/engine/sql-driver.js +1 -0
- package/dist/engine/sql-json.d.ts +7 -9
- package/dist/engine/sql-json.js +75 -15
- package/dist/engine/sql-semantics.d.ts +8 -3
- package/dist/engine/sql-semantics.js +458 -30
- package/dist/engine/typed-live.d.ts +67 -0
- package/dist/engine/typed-live.js +349 -0
- package/dist/engine/vector.d.ts +12 -2
- package/dist/engine/vector.js +635 -119
- package/dist/engine/worker-host.d.ts +9 -2
- package/dist/engine/worker-host.js +620 -152
- package/dist/engine/worker.d.ts +0 -1
- package/dist/engine/worker.js +3 -3
- package/dist/engine/write-block-planner.d.ts +18 -0
- package/dist/engine/write-block-planner.js +112 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/plan/index.d.ts +3 -4
- package/dist/plan/index.js +3 -4
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/storage/indexeddb.d.ts +99 -51
- package/dist/storage/indexeddb.js +13448 -2660
- package/dist/storage/memory.d.ts +64 -57
- package/dist/storage/memory.js +1042 -128
- package/dist/storage/opfs/files.d.ts +18 -7
- package/dist/storage/opfs/files.js +115 -28
- package/dist/storage/opfs/index.d.ts +1 -1
- package/dist/storage/opfs/index.js +1 -1
- package/dist/storage/opfs/leader.d.ts +409 -75
- package/dist/storage/opfs/leader.js +4621 -650
- package/dist/storage/opfs/rpc.d.ts +15 -3
- package/dist/storage/opfs/rpc.js +224 -2
- package/dist/storage/opfs/snapshot-ledger.d.ts +40 -0
- package/dist/storage/opfs/snapshot-ledger.js +281 -0
- package/dist/storage/opfs/store.d.ts +33 -99
- package/dist/storage/opfs/store.js +453 -364
- package/dist/storage/persistence.d.ts +37 -0
- package/dist/storage/persistence.js +78 -0
- package/dist/storage/snapshot-stream.d.ts +24 -0
- package/dist/storage/snapshot-stream.js +897 -0
- package/dist/storage/snapshot.d.ts +9 -85
- package/dist/storage/snapshot.js +33 -265
- package/dist/storage/toolkit/extents.d.ts +41 -7
- package/dist/storage/toolkit/extents.js +402 -39
- package/dist/storage/toolkit/index.d.ts +4 -5
- package/dist/storage/toolkit/index.js +28 -4
- package/dist/storage/toolkit/record-core.d.ts +182 -55
- package/dist/storage/toolkit/record-core.js +6158 -1331
- package/dist/storage/toolkit/sync-file.d.ts +10 -1
- package/dist/storage/toolkit/sync-file.js +50 -2
- package/dist/storage/toolkit/wal.d.ts +23 -5
- package/dist/storage/toolkit/wal.js +89 -27
- package/dist/storage/toolkit/wire.d.ts +15 -2
- package/dist/storage/toolkit/wire.js +199 -10
- package/dist/storage/types.d.ts +1439 -204
- package/dist/storage/types.js +1745 -141
- package/dist/testing/block-store-conformance.d.ts +0 -1
- package/dist/testing/block-store-conformance.js +1009 -119
- package/dist/testing/index.d.ts +46 -26
- package/dist/testing/index.js +112 -57
- package/dist/testing/opfs-shim.d.ts +13 -3
- package/dist/testing/opfs-shim.js +40 -6
- package/dist/testing/simulator.d.ts +97 -0
- package/dist/testing/simulator.js +591 -0
- package/dist/testing/sqllogictest.d.ts +89 -0
- package/dist/testing/sqllogictest.js +434 -0
- package/dist/transactions/index.d.ts +92 -16
- package/dist/transactions/index.js +1074 -236
- package/dist/worker-protocol/index.d.ts +3 -2
- package/dist/worker-protocol/index.js +2 -5
- package/package.json +53 -3
- package/postgres-feature-profile.json +223 -0
- package/sql-feature-matrix.json +172 -252
- package/dist/block-format/block.d.ts.map +0 -1
- package/dist/block-format/block.js.map +0 -1
- package/dist/block-format/checksum.d.ts.map +0 -1
- package/dist/block-format/checksum.js.map +0 -1
- package/dist/block-format/codecs.d.ts.map +0 -1
- package/dist/block-format/codecs.js.map +0 -1
- package/dist/block-format/column.d.ts.map +0 -1
- package/dist/block-format/column.js.map +0 -1
- package/dist/block-format/index.d.ts.map +0 -1
- package/dist/block-format/index.js.map +0 -1
- package/dist/block-format/physical.d.ts.map +0 -1
- package/dist/block-format/physical.js.map +0 -1
- package/dist/block-format/types.d.ts.map +0 -1
- package/dist/block-format/types.js.map +0 -1
- package/dist/engine/artifact-cache.d.ts.map +0 -1
- package/dist/engine/artifact-cache.js.map +0 -1
- package/dist/engine/batch.d.ts.map +0 -1
- package/dist/engine/batch.js.map +0 -1
- package/dist/engine/buffered-writer.d.ts.map +0 -1
- package/dist/engine/buffered-writer.js.map +0 -1
- package/dist/engine/catalog.d.ts.map +0 -1
- package/dist/engine/catalog.js.map +0 -1
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/coordinator.d.ts +0 -17
- package/dist/engine/coordinator.d.ts.map +0 -1
- package/dist/engine/coordinator.js +0 -60
- package/dist/engine/coordinator.js.map +0 -1
- package/dist/engine/database.d.ts.map +0 -1
- package/dist/engine/database.js.map +0 -1
- package/dist/engine/defaults.d.ts.map +0 -1
- package/dist/engine/defaults.js.map +0 -1
- package/dist/engine/errors.d.ts.map +0 -1
- package/dist/engine/errors.js.map +0 -1
- package/dist/engine/fts.d.ts.map +0 -1
- package/dist/engine/fts.js.map +0 -1
- package/dist/engine/group-index.d.ts.map +0 -1
- package/dist/engine/group-index.js.map +0 -1
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/join-index.d.ts.map +0 -1
- package/dist/engine/join-index.js.map +0 -1
- package/dist/engine/live.d.ts.map +0 -1
- package/dist/engine/live.js.map +0 -1
- package/dist/engine/memory.d.ts.map +0 -1
- package/dist/engine/memory.js.map +0 -1
- package/dist/engine/optimizer.d.ts.map +0 -1
- package/dist/engine/optimizer.js.map +0 -1
- package/dist/engine/query-cache.d.ts.map +0 -1
- package/dist/engine/query-cache.js.map +0 -1
- package/dist/engine/query.d.ts.map +0 -1
- package/dist/engine/query.js.map +0 -1
- package/dist/engine/result-wire.d.ts.map +0 -1
- package/dist/engine/result-wire.js.map +0 -1
- package/dist/engine/schema-wire.d.ts.map +0 -1
- package/dist/engine/schema-wire.js.map +0 -1
- package/dist/engine/schema.d.ts.map +0 -1
- package/dist/engine/schema.js.map +0 -1
- package/dist/engine/sort-keys.d.ts.map +0 -1
- package/dist/engine/sort-keys.js.map +0 -1
- package/dist/engine/sql-json.d.ts.map +0 -1
- package/dist/engine/sql-json.js.map +0 -1
- package/dist/engine/sql-semantics.d.ts.map +0 -1
- package/dist/engine/sql-semantics.js.map +0 -1
- package/dist/engine/vector.d.ts.map +0 -1
- package/dist/engine/vector.js.map +0 -1
- package/dist/engine/worker-host.d.ts.map +0 -1
- package/dist/engine/worker-host.js.map +0 -1
- package/dist/engine/worker.d.ts.map +0 -1
- package/dist/engine/worker.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/plan/index.d.ts.map +0 -1
- package/dist/plan/index.js.map +0 -1
- package/dist/storage/fixture-shape.d.ts +0 -42
- package/dist/storage/fixture-shape.d.ts.map +0 -1
- package/dist/storage/fixture-shape.js +0 -146
- package/dist/storage/fixture-shape.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/indexeddb.d.ts.map +0 -1
- package/dist/storage/indexeddb.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/opfs/files.d.ts.map +0 -1
- package/dist/storage/opfs/files.js.map +0 -1
- package/dist/storage/opfs/index.d.ts.map +0 -1
- package/dist/storage/opfs/index.js.map +0 -1
- package/dist/storage/opfs/leader.d.ts.map +0 -1
- package/dist/storage/opfs/leader.js.map +0 -1
- package/dist/storage/opfs/rpc.d.ts.map +0 -1
- package/dist/storage/opfs/rpc.js.map +0 -1
- package/dist/storage/opfs/store.d.ts.map +0 -1
- package/dist/storage/opfs/store.js.map +0 -1
- package/dist/storage/snapshot.d.ts.map +0 -1
- package/dist/storage/snapshot.js.map +0 -1
- package/dist/storage/toolkit/extents.d.ts.map +0 -1
- package/dist/storage/toolkit/extents.js.map +0 -1
- package/dist/storage/toolkit/index.d.ts.map +0 -1
- package/dist/storage/toolkit/index.js.map +0 -1
- package/dist/storage/toolkit/record-core.d.ts.map +0 -1
- package/dist/storage/toolkit/record-core.js.map +0 -1
- package/dist/storage/toolkit/sync-file.d.ts.map +0 -1
- package/dist/storage/toolkit/sync-file.js.map +0 -1
- package/dist/storage/toolkit/wal.d.ts.map +0 -1
- package/dist/storage/toolkit/wal.js.map +0 -1
- package/dist/storage/toolkit/wire.d.ts.map +0 -1
- package/dist/storage/toolkit/wire.js.map +0 -1
- package/dist/storage/types.d.ts.map +0 -1
- package/dist/storage/types.js.map +0 -1
- package/dist/testing/block-store-conformance.d.ts.map +0 -1
- package/dist/testing/block-store-conformance.js.map +0 -1
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing/index.js.map +0 -1
- package/dist/testing/opfs-shim.d.ts.map +0 -1
- package/dist/testing/opfs-shim.js.map +0 -1
- package/dist/testing/seeds.d.ts +0 -11
- package/dist/testing/seeds.d.ts.map +0 -1
- package/dist/testing/seeds.js +0 -50
- package/dist/testing/seeds.js.map +0 -1
- package/dist/transactions/index.d.ts.map +0 -1
- package/dist/transactions/index.js.map +0 -1
- package/dist/worker-protocol/index.d.ts.map +0 -1
- package/dist/worker-protocol/index.js.map +0 -1
package/dist/engine/memory.d.ts
CHANGED
|
@@ -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
|
package/dist/engine/memory.js
CHANGED
|
@@ -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 =
|
|
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
|
package/dist/engine/optimizer.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
|
706
|
+
const comparison = {
|
|
528
707
|
kind: "condition",
|
|
529
|
-
operator:
|
|
530
|
-
left:
|
|
531
|
-
right:
|
|
708
|
+
operator: key.operator,
|
|
709
|
+
left: keyReference(index),
|
|
710
|
+
right: key.outer,
|
|
532
711
|
};
|
|
533
712
|
on =
|
|
534
|
-
on === undefined
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
745
|
-
const right = rightValue instanceof Date ? 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
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
87
|
+
return [4, dateMilliseconds(value)];
|
|
85
88
|
throw new TypeError("Query parameter has an unsupported value");
|
|
86
89
|
}
|
|
87
|
-
//# sourceMappingURL=query-cache.js.map
|