@minnowdb/core 0.7.2 → 0.7.7
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/dist/engine/client.d.ts +2 -2
- package/dist/engine/client.js +7 -2
- package/dist/engine/database.d.ts +11 -1
- package/dist/engine/database.js +642 -175
- package/dist/engine/optimizer.js +48 -26
- package/dist/engine/query.d.ts +98 -0
- package/dist/engine/query.js +1452 -165
- package/dist/engine/sql-domains.d.ts +11 -0
- package/dist/engine/sql-domains.js +56 -0
- package/dist/engine/sql-functions.js +129 -3
- package/dist/engine/sql-json.d.ts +2 -0
- package/dist/engine/sql-json.js +4 -0
- package/dist/engine/sql-semantics.d.ts +9 -1
- package/dist/engine/sql-semantics.js +19 -3
- package/dist/engine/vector.js +29 -13
- package/dist/engine/worker-server.js +4 -1
- package/dist/plan/model.d.ts +14 -2
- package/dist/storage/indexeddb.js +13 -8
- package/dist/storage/memory.d.ts +2 -1
- package/dist/storage/memory.js +29 -4
- package/dist/storage/toolkit/record-core.js +11 -6
- package/dist/storage/types.d.ts +11 -0
- package/dist/testing/sqllogictest.js +3 -1
- package/package.json +1 -1
- package/postgres-feature-profile.json +9 -4
- package/sql-feature-matrix.json +541 -4
package/dist/engine/optimizer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dateIsoString, dateMilliseconds } from "../date-value.js";
|
|
2
2
|
import { crossJoinPlan } from "../plan/model.js";
|
|
3
|
-
import { blockHasSubqueries, childExpressions, DUAL_TABLE, expressionAliases, forEachBlockExpression, forEachNestedBlock, hasAggregate, isAggregateCall, isScalarFunctionName, mapBlockExpressions, mapChildExpressions, parseQuantified, scalarFunctionNames, scalarFunctionValue, splitCondition, statementDatetimeNames, volatileScalarFunctionNames, transparentProjectionSource, dateTruncValue } from "./query.js";
|
|
3
|
+
import { blockHasRowWindow, blockHasSubqueries, childExpressions, DUAL_TABLE, expressionAliases, forEachBlockExpression, forEachNestedBlock, hasAggregate, isAggregateCall, isScalarFunctionName, mapBlockExpressions, mapChildExpressions, parseQuantified, scalarFunctionNames, scalarFunctionValue, splitCondition, statementDatetimeNames, volatileScalarFunctionNames, transparentProjectionSource, dateTruncValue, integerQuotient } from "./query.js";
|
|
4
4
|
import { concatenatedSqlValue, isSqlDomainValue } from "./sql-domains.js";
|
|
5
5
|
import { simpleScalarFunctions } from "./sql-functions.js";
|
|
6
6
|
function optimizePlan(plan, options = {}) {
|
|
@@ -199,7 +199,7 @@ function decorrelateLateralSources(block) {
|
|
|
199
199
|
const keyAliases = keys.map((_, index) => `\0lateral_key_${String(index + 1)}`);
|
|
200
200
|
const grouped = inner.groupBy.length > 0;
|
|
201
201
|
const globalAggregate = !grouped && inner.select.some((item) => containsAggregateCall(item.expression)) && inner.having.length === 0;
|
|
202
|
-
const ranked = inner
|
|
202
|
+
const ranked = blockHasRowWindow(inner);
|
|
203
203
|
const cross = join.on?.kind === "condition" && join.on.operator === "=" && join.on.left.kind === "literal" && join.on.left.value === 1 && join.on.right.kind === "literal" && join.on.right.value === 1 || join.on?.kind === "literal" && join.on.value === true;
|
|
204
204
|
if (grouped || globalAggregate || ranked || inner.having.length > 0) {
|
|
205
205
|
if (!keys.every((key) => key.operator === "=")) {
|
|
@@ -1409,7 +1409,7 @@ function decorrelateScalarSubquery(block, subquery, scope, nextAlias) {
|
|
|
1409
1409
|
if (passthrough !== void 0)
|
|
1410
1410
|
return passthrough;
|
|
1411
1411
|
const item = inner.select[0];
|
|
1412
|
-
if (inner.select.length !== 1 || item === void 0 || !isAggregateCall(item.expression) || inner.groupBy.length > 0 || inner.having.length > 0 || inner.orderBy.length > 0 || inner
|
|
1412
|
+
if (inner.select.length !== 1 || item === void 0 || !isAggregateCall(item.expression) || inner.groupBy.length > 0 || inner.having.length > 0 || inner.orderBy.length > 0 || blockHasRowWindow(inner) || !canExtractCorrelation(inner, scope, "scalar", true)) {
|
|
1413
1413
|
return decorrelateGeneralScalar(block, inner, scope, nextAlias);
|
|
1414
1414
|
}
|
|
1415
1415
|
const previewKeys = extractCorrelation(structuredClone(inner), scope, "scalar", true);
|
|
@@ -1432,7 +1432,10 @@ function decorrelateScalarSubquery(block, subquery, scope, nextAlias) {
|
|
|
1432
1432
|
})),
|
|
1433
1433
|
{ expression: item.expression, alias: correlationValueAlias }
|
|
1434
1434
|
],
|
|
1435
|
-
predicates:
|
|
1435
|
+
predicates: [
|
|
1436
|
+
...inner.predicates,
|
|
1437
|
+
...mirrorPredicatesThroughKeys(outerProbePredicates(block, keys.map((key) => key.outer), nextAlias.resolvedNames), keys)
|
|
1438
|
+
],
|
|
1436
1439
|
groupBy: keys.map((key) => key.inner),
|
|
1437
1440
|
having: [],
|
|
1438
1441
|
orderBy: []
|
|
@@ -1449,7 +1452,7 @@ function decorrelateScalarSubquery(block, subquery, scope, nextAlias) {
|
|
|
1449
1452
|
}
|
|
1450
1453
|
function scalarPassthrough(inner) {
|
|
1451
1454
|
const item = inner.select[0];
|
|
1452
|
-
if (inner.base.table !== DUAL_TABLE || inner.base.derived !== void 0 || inner.joins.length > 0 || inner.select.length !== 1 || item === void 0 || inner.predicates.length > 0 || inner.groupBy.length > 0 || inner.having.length > 0 || inner.orderBy.length > 0 || inner.offset !== void 0 || inner.limit !== void 0 && inner.limit < 1) {
|
|
1455
|
+
if (inner.base.table !== DUAL_TABLE || inner.base.derived !== void 0 || inner.joins.length > 0 || inner.select.length !== 1 || item === void 0 || inner.predicates.length > 0 || inner.groupBy.length > 0 || inner.having.length > 0 || inner.orderBy.length > 0 || inner.offset !== void 0 || inner.offsetParameter !== void 0 || inner.limitParameter !== void 0 || inner.limit !== void 0 && inner.limit < 1) {
|
|
1453
1456
|
return void 0;
|
|
1454
1457
|
}
|
|
1455
1458
|
return structuredClone(item.expression);
|
|
@@ -1463,7 +1466,7 @@ function scalarShape(inner) {
|
|
|
1463
1466
|
let expression = structuredClone(item.expression);
|
|
1464
1467
|
for (; ; ) {
|
|
1465
1468
|
const derived = rows.base.derived;
|
|
1466
|
-
if (derived === void 0 || rows.joins.length > 0 || rows.predicates.length > 0 || rows.groupBy.length > 0 || rows.having.length > 0 || rows.orderBy.length > 0 || rows
|
|
1469
|
+
if (derived === void 0 || rows.joins.length > 0 || rows.predicates.length > 0 || rows.groupBy.length > 0 || rows.having.length > 0 || rows.orderBy.length > 0 || blockHasRowWindow(rows) || rows.distinctWildcard === true) {
|
|
1467
1470
|
break;
|
|
1468
1471
|
}
|
|
1469
1472
|
expression = inlineDerivedProjection(expression, rows.base.alias, derived);
|
|
@@ -1656,7 +1659,7 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
|
|
|
1656
1659
|
orderBy: []
|
|
1657
1660
|
};
|
|
1658
1661
|
let finalRows = matched;
|
|
1659
|
-
if (rows
|
|
1662
|
+
if (blockHasRowWindow(rows)) {
|
|
1660
1663
|
const rankedAlias = nextAlias();
|
|
1661
1664
|
const rankedTable = nextAlias();
|
|
1662
1665
|
const ranked = {
|
|
@@ -2022,6 +2025,34 @@ function outerProbeBlock(block, keys, resolvedNames) {
|
|
|
2022
2025
|
return outerProbeBlockForExpressions(block, keys.map((key) => key.outer), resolvedNames);
|
|
2023
2026
|
}
|
|
2024
2027
|
function outerProbeBlockForExpressions(block, outerExpressions, resolvedNames) {
|
|
2028
|
+
const prefix = outerProbePrefix(block, outerExpressions, resolvedNames);
|
|
2029
|
+
const expressions = outerExpressions.map((expression) => structuredClone(expression));
|
|
2030
|
+
return {
|
|
2031
|
+
sql: "(correlation probes)",
|
|
2032
|
+
base: structuredClone(block.base),
|
|
2033
|
+
joins: structuredClone(block.joins.slice(0, prefix.lastSource)),
|
|
2034
|
+
select: expressions.map((expression, index) => ({
|
|
2035
|
+
expression,
|
|
2036
|
+
alias: correlationProbeAlias(index)
|
|
2037
|
+
})),
|
|
2038
|
+
predicates: probePredicates(block, prefix.retained, prefix.retainsAll),
|
|
2039
|
+
groupBy: expressions,
|
|
2040
|
+
having: [],
|
|
2041
|
+
orderBy: []
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
function outerProbePredicates(block, outerExpressions, resolvedNames) {
|
|
2045
|
+
const sources = /* @__PURE__ */ new Set([block.base.alias, ...block.joins.map((join) => join.alias)]);
|
|
2046
|
+
for (const expression of outerExpressions) {
|
|
2047
|
+
for (const alias of expressionAliases(expression)) {
|
|
2048
|
+
if (!sources.has(alias))
|
|
2049
|
+
return [];
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
const prefix = outerProbePrefix(block, outerExpressions, resolvedNames);
|
|
2053
|
+
return probePredicates(block, prefix.retained, prefix.retainsAll);
|
|
2054
|
+
}
|
|
2055
|
+
function outerProbePrefix(block, outerExpressions, resolvedNames) {
|
|
2025
2056
|
const sources = [block.base, ...block.joins];
|
|
2026
2057
|
const sourceIndexes = outerExpressions.flatMap((expression) => {
|
|
2027
2058
|
const aliases = [...expressionAliases(expression)];
|
|
@@ -2035,22 +2066,9 @@ function outerProbeBlockForExpressions(block, outerExpressions, resolvedNames) {
|
|
|
2035
2066
|
});
|
|
2036
2067
|
});
|
|
2037
2068
|
const lastSource = Math.max(...sourceIndexes);
|
|
2038
|
-
const expressions = outerExpressions.map((expression) => structuredClone(expression));
|
|
2039
2069
|
const retained = new Set(sources.slice(0, lastSource + 1).map((source) => source.alias));
|
|
2040
2070
|
const retainsAll = resolvedNames && lastSource === sources.length - 1;
|
|
2041
|
-
return {
|
|
2042
|
-
sql: "(correlation probes)",
|
|
2043
|
-
base: structuredClone(block.base),
|
|
2044
|
-
joins: structuredClone(block.joins.slice(0, lastSource)),
|
|
2045
|
-
select: expressions.map((expression, index) => ({
|
|
2046
|
-
expression,
|
|
2047
|
-
alias: correlationProbeAlias(index)
|
|
2048
|
-
})),
|
|
2049
|
-
predicates: probePredicates(block, retained, retainsAll),
|
|
2050
|
-
groupBy: expressions,
|
|
2051
|
-
having: [],
|
|
2052
|
-
orderBy: []
|
|
2053
|
-
};
|
|
2071
|
+
return { lastSource, retained, retainsAll };
|
|
2054
2072
|
}
|
|
2055
2073
|
function probePredicates(block, retained, retainsAll) {
|
|
2056
2074
|
return block.predicates.filter((predicate) => probeSafeExpression(predicate.left, retained, retainsAll) && probeSafeExpression(predicate.right, retained, retainsAll)).map((predicate) => structuredClone(predicate));
|
|
@@ -2387,14 +2405,15 @@ function foldExpression(expression) {
|
|
|
2387
2405
|
const left = foldExpression(expression.left);
|
|
2388
2406
|
const right = foldExpression(expression.right);
|
|
2389
2407
|
if (left.kind === "literal" && right.kind === "literal") {
|
|
2390
|
-
const folded = foldBinary(expression.operator, left.value, right.value);
|
|
2408
|
+
const folded = foldBinary(expression.operator, left.value, right.value, expression.integer === true);
|
|
2391
2409
|
if (folded !== void 0) {
|
|
2392
2410
|
const domain = left.sqlDomain?.kind === "numeric" || right.sqlDomain?.kind === "numeric" ? { kind: "numeric" } : void 0;
|
|
2393
2411
|
return {
|
|
2394
2412
|
kind: "literal",
|
|
2395
2413
|
value: folded,
|
|
2396
2414
|
...isSqlDomainValue(folded) ? { internalSqlValue: true } : {},
|
|
2397
|
-
...domain === void 0 ? {} : { sqlDomain: domain }
|
|
2415
|
+
...domain === void 0 ? {} : { sqlDomain: domain },
|
|
2416
|
+
...left.decimal === true || right.decimal === true ? { decimal: true } : {}
|
|
2398
2417
|
};
|
|
2399
2418
|
}
|
|
2400
2419
|
}
|
|
@@ -2466,7 +2485,7 @@ function foldExpression(expression) {
|
|
|
2466
2485
|
}
|
|
2467
2486
|
return expression;
|
|
2468
2487
|
}
|
|
2469
|
-
function foldBinary(operator, leftValue, rightValue) {
|
|
2488
|
+
function foldBinary(operator, leftValue, rightValue, integer = false) {
|
|
2470
2489
|
if (leftValue === null || rightValue === null)
|
|
2471
2490
|
return null;
|
|
2472
2491
|
if (operator === "||") {
|
|
@@ -2487,6 +2506,9 @@ function foldBinary(operator, leftValue, rightValue) {
|
|
|
2487
2506
|
const right = rightValue instanceof Date ? dateMilliseconds(rightValue) : rightValue;
|
|
2488
2507
|
if ((operator === "/" || operator === "%") && right === 0)
|
|
2489
2508
|
return null;
|
|
2509
|
+
if (operator === "/" && integer && typeof left === "number" && typeof right === "number") {
|
|
2510
|
+
return integerQuotient(left, right);
|
|
2511
|
+
}
|
|
2490
2512
|
const result = operator === "+" ? left + right : operator === "-" ? left - right : operator === "*" ? left * right : operator === "%" ? left % right : left / right;
|
|
2491
2513
|
return Number.isFinite(result) ? result : void 0;
|
|
2492
2514
|
}
|
|
@@ -2508,7 +2530,7 @@ function pushPredicatesIntoDerived(block) {
|
|
|
2508
2530
|
function pushdownTarget(predicate, sources, singleSource) {
|
|
2509
2531
|
for (const source of sources) {
|
|
2510
2532
|
const derived = source.derived;
|
|
2511
|
-
if (derived === void 0 || derived
|
|
2533
|
+
if (derived === void 0 || blockHasRowWindow(derived))
|
|
2512
2534
|
continue;
|
|
2513
2535
|
const left = rewriteForInner(predicate.left, source, derived, singleSource);
|
|
2514
2536
|
if (left === void 0)
|
|
@@ -2740,7 +2762,7 @@ function referencedOutputAliases(block, source, singleSource) {
|
|
|
2740
2762
|
}
|
|
2741
2763
|
function combineDerivedLimit(block) {
|
|
2742
2764
|
const derived = block.base.derived;
|
|
2743
|
-
if (derived === void 0 || block.joins.length > 0 || block.predicates.length > 0 || block.groupBy.length > 0 || block.having.length > 0 || block.orderBy.length > 0 || block.limit === void 0 || block.offset !== void 0 || derived.offset !== void 0) {
|
|
2765
|
+
if (derived === void 0 || block.joins.length > 0 || block.predicates.length > 0 || block.groupBy.length > 0 || block.having.length > 0 || block.orderBy.length > 0 || block.limit === void 0 || block.offset !== void 0 || block.offsetParameter !== void 0 || derived.offset !== void 0 || derived.offsetParameter !== void 0 || derived.limitParameter !== void 0) {
|
|
2744
2766
|
return;
|
|
2745
2767
|
}
|
|
2746
2768
|
derived.limit = Math.min(derived.limit ?? Number.MAX_SAFE_INTEGER, block.limit);
|
package/dist/engine/query.d.ts
CHANGED
|
@@ -135,6 +135,13 @@ export type InsertValue = QueryValue | DefaultInsertValue | DeferredInsertExpres
|
|
|
135
135
|
};
|
|
136
136
|
export declare function isDefaultInsertValue(value: InsertValue): value is DefaultInsertValue;
|
|
137
137
|
export declare function isDeferredInsertExpression(value: InsertValue): value is DeferredInsertExpression;
|
|
138
|
+
/** The FROM / USING sources of an UPDATE or DELETE, joined to the target as a cross product. */
|
|
139
|
+
export interface MutationSources {
|
|
140
|
+
base: TableSource;
|
|
141
|
+
joins: JoinPlan[];
|
|
142
|
+
/** ON conditions of parenthesized join groups, which filter the product like WHERE. */
|
|
143
|
+
predicates: Predicate[];
|
|
144
|
+
}
|
|
138
145
|
export type CompiledStatement = {
|
|
139
146
|
kind: "select";
|
|
140
147
|
sql: string;
|
|
@@ -189,6 +196,12 @@ export type CompiledStatement = {
|
|
|
189
196
|
operator: PredicateOperator;
|
|
190
197
|
right: Expression;
|
|
191
198
|
}>;
|
|
199
|
+
/**
|
|
200
|
+
* `UPDATE t SET … FROM sources WHERE …`: the extra sources the assignments and predicates
|
|
201
|
+
* may read, joined to the target as PostgreSQL joins them. A target row matched by several
|
|
202
|
+
* source rows is updated once, from the first match.
|
|
203
|
+
*/
|
|
204
|
+
from?: MutationSources;
|
|
192
205
|
returning?: string[] | "*";
|
|
193
206
|
/** RETURNING items that are not plain columns, evaluated over the affected rows. */
|
|
194
207
|
returningItems?: ReturningItem[];
|
|
@@ -202,6 +215,8 @@ export type CompiledStatement = {
|
|
|
202
215
|
operator: PredicateOperator;
|
|
203
216
|
right: Expression;
|
|
204
217
|
}>;
|
|
218
|
+
/** `DELETE FROM t USING sources WHERE …`: the extra sources the predicates may read. */
|
|
219
|
+
from?: MutationSources;
|
|
205
220
|
returning?: string[] | "*";
|
|
206
221
|
/** RETURNING items that are not plain columns, evaluated over the affected rows. */
|
|
207
222
|
returningItems?: ReturningItem[];
|
|
@@ -303,6 +318,19 @@ export type CompiledStatement = {
|
|
|
303
318
|
view: string;
|
|
304
319
|
ifExists?: boolean;
|
|
305
320
|
parameterCount?: number;
|
|
321
|
+
}
|
|
322
|
+
/** A session setting, accepted and ignored: `SET name TO value`, `RESET name`. */
|
|
323
|
+
| {
|
|
324
|
+
kind: "set";
|
|
325
|
+
action: "set" | "reset";
|
|
326
|
+
name: string;
|
|
327
|
+
parameterCount?: number;
|
|
328
|
+
}
|
|
329
|
+
/** `SHOW name`: the engine's value for a PostgreSQL setting, as one row. */
|
|
330
|
+
| {
|
|
331
|
+
kind: "show";
|
|
332
|
+
name: string;
|
|
333
|
+
parameterCount?: number;
|
|
306
334
|
} | {
|
|
307
335
|
kind: "transaction";
|
|
308
336
|
action: "begin" | "commit" | "rollback" | "savepoint" | "rollback-to" | "release";
|
|
@@ -487,6 +515,64 @@ export declare function mapChildExpressions(expression: Expression, map: (child:
|
|
|
487
515
|
export declare function forEachBlockExpression(block: CompiledQuery, visit: (expression: Expression) => void): void;
|
|
488
516
|
/** Visits every nested block of one block's sources (derived, union, windowed, recursive). */
|
|
489
517
|
export declare function forEachNestedBlock(block: CompiledQuery, visit: (nested: CompiledQuery) => void): void;
|
|
518
|
+
/**
|
|
519
|
+
* Whether an expression is integer-typed the way PostgreSQL types it: an INTEGER, BIGINT, or
|
|
520
|
+
* SMALLINT column, an integer constant, COUNT, an integer CAST, SUM/MIN/MAX/ABS over an
|
|
521
|
+
* integer, MOD over integers, a CASE/COALESCE/NULLIF/GREATEST/LEAST whose every branch is an
|
|
522
|
+
* integer, or `+ - * / %` over two integers. A bound parameter takes the type of its integer
|
|
523
|
+
* partner, as PostgreSQL infers `qty / $1`. A column the caller cannot type is not an integer,
|
|
524
|
+
* so the compiler's static answer only ever widens once the catalog is known.
|
|
525
|
+
*/
|
|
526
|
+
export declare function integerTypedExpression(expression: Expression, columnInteger?: (reference: string) => boolean, tableIntegerColumns?: (table: string) => IntegerColumns | undefined): boolean;
|
|
527
|
+
/** A table's integer columns, or `true` when every column may be one (the bind-time probe). */
|
|
528
|
+
export type IntegerColumns = ReadonlySet<string> | true;
|
|
529
|
+
/** PostgreSQL's integer `/`: truncation toward zero, NULL for a zero divisor. */
|
|
530
|
+
export declare function integerQuotient(left: number, right: number): number | null;
|
|
531
|
+
/**
|
|
532
|
+
* Marks every `/` in one expression whose operands are integers, subquery blocks included, so
|
|
533
|
+
* that every evaluator truncates it the way PostgreSQL does. Children are marked first: the
|
|
534
|
+
* parent's typing reads the children, and `(a / b) / c` is integer only once `a / b` is.
|
|
535
|
+
*/
|
|
536
|
+
export declare function annotateIntegerDivision(expression: Expression, columnInteger?: (reference: string) => boolean, tableIntegerColumns?: (table: string) => IntegerColumns | undefined): void;
|
|
537
|
+
/**
|
|
538
|
+
* Marks integer division throughout a plan, nested blocks first. Without a catalog only
|
|
539
|
+
* constants, COUNT, and integer CASTs type as integers; with one, INTEGER columns do too, and
|
|
540
|
+
* a derived source's outputs follow from its own select list.
|
|
541
|
+
*/
|
|
542
|
+
export declare function annotatePlanIntegerDivision(plan: CompiledQuery, tableIntegerColumns?: (table: string) => IntegerColumns | undefined): void;
|
|
543
|
+
/**
|
|
544
|
+
* Whether catalog binding could still mark a division in this plan: a `/` not yet marked whose
|
|
545
|
+
* operands would be integers if every column were one. Plans without such a node — the common
|
|
546
|
+
* case — need no clone at bind time.
|
|
547
|
+
*/
|
|
548
|
+
export declare function planMayHaveIntegerDivision(plan: CompiledQuery): boolean;
|
|
549
|
+
/**
|
|
550
|
+
* PostgreSQL folds an unquoted identifier to lower case, so `SELECT ID FROM USERS` reads the
|
|
551
|
+
* table `users` and its column `id`. Minnow keeps the written spelling and resolves it exactly,
|
|
552
|
+
* which is what a catalog created through the typed API (where `Orders` is a legal name)
|
|
553
|
+
* relies on. This pass gives both: a table or column name that resolves exactly is left alone,
|
|
554
|
+
* and one that does not is read as the unique catalog name that matches it case-insensitively.
|
|
555
|
+
* Only plain tables and their columns are folded; derived sources expose their aliases as
|
|
556
|
+
* written. Returns the same plan when nothing needed folding, so cached plans stay untouched.
|
|
557
|
+
*/
|
|
558
|
+
export declare function foldIdentifierCase(plan: CompiledQuery, tables: ReadonlyMap<string, readonly string[]>): CompiledQuery;
|
|
559
|
+
/**
|
|
560
|
+
* A table alias used as a value — `json_agg(agg)`, `to_json(obj)`, `row_to_json(u)` — is the
|
|
561
|
+
* row as a JSON object in PostgreSQL. The reference is expanded here, once the catalog is
|
|
562
|
+
* known, into JSON_OBJECT over the source's columns in declaration order, so the aggregate and
|
|
563
|
+
* constructor paths see a scalar document. Only an alias of the same block that names no column
|
|
564
|
+
* expands; a bare name that is also a column stays that column.
|
|
565
|
+
*/
|
|
566
|
+
export declare function expandRowReferences(plan: CompiledQuery, tableColumns: (table: string) => readonly string[] | undefined): CompiledQuery;
|
|
567
|
+
/**
|
|
568
|
+
* PostgreSQL's functional-dependency rule (SQL:2003 T301): once a table's whole primary key is
|
|
569
|
+
* in GROUP BY, any other column of that table may appear ungrouped, because the key determines
|
|
570
|
+
* the row. Minnow's executors group by the listed expressions only, so such a column is added
|
|
571
|
+
* to GROUP BY here — the groups are unchanged, since the key already separates every row — and
|
|
572
|
+
* the containment check then accepts it. Only plain table sources with a declared key
|
|
573
|
+
* qualify; a derived table exposes no key. Returns the same plan when nothing needed adding.
|
|
574
|
+
*/
|
|
575
|
+
export declare function extendGroupByWithKeyDependents(plan: CompiledQuery, keyColumns: (table: string) => readonly string[] | undefined, tableColumns?: (table: string) => readonly string[] | undefined): CompiledQuery;
|
|
490
576
|
/**
|
|
491
577
|
* Rewrites every expression slot of one block in place, the writing counterpart to
|
|
492
578
|
* `forEachBlockExpression`. The mapper receives each root expression and returns its
|
|
@@ -650,6 +736,11 @@ interface SelectBlockParts {
|
|
|
650
736
|
/** GROUP BY GROUPING SETS/ROLLUP/CUBE: the grouping lists to union; groupBy is then unused. */
|
|
651
737
|
groupingSets?: Expression[][];
|
|
652
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* The execution-time half of unifyGroupedReferences for a block over several sources: an
|
|
741
|
+
* unqualified name is the column of the one source that has it, which only the schemas know.
|
|
742
|
+
*/
|
|
743
|
+
export declare function unifyPlanGroupedReferences(plan: CompiledQuery, columnsOf: (tableName: string) => readonly string[] | undefined): CompiledQuery;
|
|
653
744
|
export declare function assembleSelectBlock(parts: SelectBlockParts, nextSequence: () => number): CompiledQuery;
|
|
654
745
|
/**
|
|
655
746
|
* Turns every source carrying a column alias list into a derived projection that renames the
|
|
@@ -688,6 +779,13 @@ export declare function expandNaturalJoins(plan: CompiledQuery, columnsOf: (tabl
|
|
|
688
779
|
export declare function expandSourceColumnAliases(plan: CompiledQuery, columnsOf: (tableName: string) => readonly string[] | undefined): CompiledQuery;
|
|
689
780
|
/** Wraps compound members into the set-operation source the executor folds left to right. */
|
|
690
781
|
export declare function compoundSelectBlock(sql: string, blocks: CompiledQuery[], ops: SetOperator[], tail: SelectTail, nextSequence: () => number): CompiledQuery;
|
|
782
|
+
/**
|
|
783
|
+
* True when a block trims its rows with LIMIT, OFFSET, or a placeholder for either. A rewrite
|
|
784
|
+
* that moves a filter or a limit across such a block changes which rows the window sees, so every
|
|
785
|
+
* "does this block page its rows" guard goes through here: reading only the literal fields lets
|
|
786
|
+
* `LIMIT ?`, `OFFSET ?`, and a bare `OFFSET n` slip past and the predicate run before the window.
|
|
787
|
+
*/
|
|
788
|
+
export declare function blockHasRowWindow(block: CompiledQuery): boolean;
|
|
691
789
|
/**
|
|
692
790
|
* Detects a pure projection wrapper over one derived block — the shape the ORDER-BY-expression
|
|
693
791
|
* desugar emits: no joins, filters, grouping, ordering, or paging of its own, and every select
|