@minnowdb/core 0.7.1 → 0.7.5

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.
@@ -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.limit !== void 0 || inner.limitParameter !== void 0 || inner.offset !== void 0 || inner.offsetParameter !== void 0;
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.limit !== void 0 || inner.offset !== void 0 || !canExtractCorrelation(inner, scope, "scalar", true)) {
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: inner.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.limit !== void 0 || rows.limitParameter !== void 0 || rows.offset !== void 0 || rows.offsetParameter !== void 0 || rows.distinctWildcard === true) {
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);
@@ -1494,14 +1497,17 @@ function scalarOrderExpression(expression, rows) {
1494
1497
  const matches = rows.select.filter((item) => item.alias === expression.reference);
1495
1498
  return matches.length === 1 ? structuredClone(matches[0]?.expression ?? expression) : structuredClone(expression);
1496
1499
  }
1497
- function emptyScalarExpression(expression) {
1500
+ function emptyScalarExpression(expression, preservedColumns = /* @__PURE__ */ new Set()) {
1498
1501
  if (isAggregateCall(expression)) {
1499
1502
  return { kind: "literal", value: expression.name === "COUNT" ? 0 : null };
1500
1503
  }
1501
- if (expression.kind === "column" || expression.kind === "wildcard") {
1504
+ if (expression.kind === "column") {
1505
+ return preservedColumns.has(expression.reference) ? structuredClone(expression) : { kind: "literal", value: null };
1506
+ }
1507
+ if (expression.kind === "wildcard") {
1502
1508
  return { kind: "literal", value: null };
1503
1509
  }
1504
- return mapChildExpressions(expression, emptyScalarExpression);
1510
+ return mapChildExpressions(expression, (child) => emptyScalarExpression(child, preservedColumns));
1505
1511
  }
1506
1512
  function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1507
1513
  const shape = scalarShape(inner);
@@ -1512,14 +1518,44 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1512
1518
  if (rows.limitWithTies === true && rows.orderBy.length === 0) {
1513
1519
  throw new TypeError("A correlated scalar WITH TIES query requires ORDER BY");
1514
1520
  }
1515
- const keys = extractCorrelation(rows, scope, "scalar", true);
1516
- const outerExpressions = keys.map((key) => structuredClone(key.outer));
1521
+ const expressionOutside = [];
1522
+ collectExpressionOutsideReferences(shape.expression, /* @__PURE__ */ new Set([rows.base.alias, ...rows.joins.map((join) => join.alias)]), expressionOutside);
1523
+ const probeLifted = expressionOutside.length > 0 || !canExtractCorrelation(rows, scope, "scalar", true);
1524
+ const references = probeLifted ? probeOuterReferences(rows, expressionOutside, scope, "scalar") : [];
1525
+ const keys = probeLifted ? takeCorrelationPredicates(rows, scope, true) : extractCorrelation(rows, scope, "scalar", true);
1526
+ const outerExpressions = probeLifted ? references.map((reference) => ({ kind: "column", reference })) : keys.map((key) => structuredClone(key.outer));
1527
+ const originalExpression = structuredClone(shape.expression);
1517
1528
  const probes = outerProbeBlockForExpressions(block, outerExpressions, nextAlias.resolvedNames);
1518
1529
  const probesAlias = nextAlias();
1519
1530
  const probeReferences = outerExpressions.map((_, index) => ({
1520
1531
  kind: "column",
1521
1532
  reference: `${probesAlias}.${correlationProbeAlias(index)}`
1522
1533
  }));
1534
+ if (probeLifted) {
1535
+ const replacements = new Map(references.map((reference, index) => [reference, probeReferences[index] ?? null]));
1536
+ replacePlanReferences(rows, replacements);
1537
+ shape.expression = replaceExpressionReferences(shape.expression, replacements);
1538
+ const source = { table: probesAlias, alias: probesAlias, derived: probes };
1539
+ const missingProbe = { kind: "literal", value: null };
1540
+ const keyProbes = keys.map((key) => {
1541
+ const reference = key.outer.kind === "column" ? key.outer.reference : "";
1542
+ return probeReferences[references.indexOf(reference)] ?? missingProbe;
1543
+ });
1544
+ const keysReadBase = keys.every((key) => {
1545
+ const parts = key.inner.kind === "column" ? key.inner.reference.split(".") : [];
1546
+ return parts.length === 1 || parts[0] === rows.base.alias;
1547
+ });
1548
+ if (keys.length > 0 && keysReadBase) {
1549
+ rows.joins.unshift(generalCorrelationJoin(probesAlias, probes, keys.map((key) => key.inner), keyProbes, keys.map((key) => key.operator)));
1550
+ } else {
1551
+ rows.joins.unshift(crossJoinPlan(source));
1552
+ rows.predicates.push(...keys.map((key, index) => ({
1553
+ left: key.inner,
1554
+ operator: key.operator,
1555
+ right: keyProbes[index] ?? missingProbe
1556
+ })));
1557
+ }
1558
+ }
1523
1559
  const finalRowsAlias = nextAlias();
1524
1560
  const projected = [];
1525
1561
  const project = (expression, alias) => {
@@ -1556,6 +1592,15 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1556
1592
  return rewritten;
1557
1593
  }
1558
1594
  if (expression.kind === "column" || expression.kind === "wildcard") {
1595
+ if (probeLifted && expression.kind === "column") {
1596
+ const index = probeReferences.findIndex((reference) => reference.kind === "column" && reference.reference === expression.reference);
1597
+ if (index >= 0) {
1598
+ return {
1599
+ kind: "column",
1600
+ reference: `${finalRowsAlias}.${correlationKeyAlias(index)}`
1601
+ };
1602
+ }
1603
+ }
1559
1604
  throw new TypeError("A correlated scalar aggregate cannot select an ungrouped inner column");
1560
1605
  }
1561
1606
  if (expression.kind === "subquery" || expression.kind === "exists") {
@@ -1575,18 +1620,21 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1575
1620
  base: rows.base,
1576
1621
  joins: rows.joins,
1577
1622
  select: [
1578
- ...keys.map((key, index) => ({
1579
- expression: key.inner,
1623
+ ...(probeLifted ? probeReferences : keys.map((key) => key.inner)).map((expression, index) => ({
1624
+ expression,
1580
1625
  alias: correlationKeyAlias(index)
1581
1626
  })),
1582
1627
  ...projected
1583
1628
  ],
1584
- predicates: [...rows.predicates, ...mirrorPredicatesThroughKeys(probes.predicates, keys)],
1629
+ predicates: [
1630
+ ...rows.predicates,
1631
+ ...probeLifted ? [] : mirrorPredicatesThroughKeys(probes.predicates, keys)
1632
+ ],
1585
1633
  groupBy: [],
1586
1634
  having: [],
1587
1635
  orderBy: []
1588
1636
  };
1589
- const matched = {
1637
+ const matched = probeLifted ? rawRows : {
1590
1638
  sql: "(correlated scalar matched probes)",
1591
1639
  base: { table: probesAlias, alias: probesAlias, derived: probes },
1592
1640
  joins: [
@@ -1611,7 +1659,7 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1611
1659
  orderBy: []
1612
1660
  };
1613
1661
  let finalRows = matched;
1614
- if (rows.limit !== void 0 || rows.limitParameter !== void 0 || rows.offset !== void 0 || rows.offsetParameter !== void 0) {
1662
+ if (blockHasRowWindow(rows)) {
1615
1663
  const rankedAlias = nextAlias();
1616
1664
  const rankedTable = nextAlias();
1617
1665
  const ranked = {
@@ -1623,7 +1671,7 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1623
1671
  {
1624
1672
  alias: correlationRowNumberAlias,
1625
1673
  name: rows.limitWithTies === true ? "RANK" : "ROW_NUMBER",
1626
- partitionAliases: keys.map((_, index) => correlationKeyAlias(index)),
1674
+ partitionAliases: outerExpressions.map((_, index) => correlationKeyAlias(index)),
1627
1675
  orderAliases: rows.orderBy.map((order, index) => ({
1628
1676
  alias: correlationOrderAlias(index),
1629
1677
  direction: order.direction,
@@ -1659,7 +1707,7 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1659
1707
  base: ranked,
1660
1708
  joins: [],
1661
1709
  select: [
1662
- ...keys.map((_, index) => ({
1710
+ ...outerExpressions.map((_, index) => ({
1663
1711
  expression: {
1664
1712
  kind: "column",
1665
1713
  reference: `${rankedAlias}.${correlationKeyAlias(index)}`
@@ -1679,7 +1727,7 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1679
1727
  ...rows.offsetParameter === void 0 ? {} : { offsetValidationParameters: [rows.offsetParameter] }
1680
1728
  };
1681
1729
  }
1682
- const finalProbeReferences = keys.map((_, index) => ({
1730
+ const finalProbeReferences = outerExpressions.map((_, index) => ({
1683
1731
  kind: "column",
1684
1732
  reference: `${finalRowsAlias}.${correlationKeyAlias(index)}`
1685
1733
  }));
@@ -1716,7 +1764,7 @@ function decorrelateGeneralScalar(block, inner, scope, nextAlias) {
1716
1764
  left: marker,
1717
1765
  right: { kind: "literal", value: null }
1718
1766
  },
1719
- then: grouped ? emptyScalarExpression(structuredClone(shape.expression)) : { kind: "literal", value: null }
1767
+ then: grouped ? emptyScalarExpression(originalExpression, new Set(references)) : { kind: "literal", value: null }
1720
1768
  }
1721
1769
  ],
1722
1770
  otherwise: {
@@ -1880,19 +1928,7 @@ function decorrelateExistsExpression(block, exists, scope, nextAlias) {
1880
1928
  }
1881
1929
  function decorrelateExistsWithProbes(block, exists, scope, nextAlias) {
1882
1930
  const inner = exists.block;
1883
- const outside = [];
1884
- collectOutsideReferences(inner, /* @__PURE__ */ new Set(), outside);
1885
- const references = [...new Set(outside)];
1886
- if (references.length === 0) {
1887
- throw new TypeError("Correlated EXISTS subquery has no usable outer reference");
1888
- }
1889
- for (const reference of references) {
1890
- const parts = reference.split(".");
1891
- const alias = parts.length === 2 ? parts[0] : void 0;
1892
- if (alias === void 0 || !scope.has(alias)) {
1893
- throw new TypeError(`Correlated EXISTS references an unavailable outer source: ${reference}`);
1894
- }
1895
- }
1931
+ const references = probeOuterReferences(inner, [], scope, "EXISTS");
1896
1932
  const outerExpressions = references.map((reference) => ({
1897
1933
  kind: "column",
1898
1934
  reference
@@ -1959,33 +1995,64 @@ function decorrelateExistsWithProbes(block, exists, scope, nextAlias) {
1959
1995
  right: { kind: "literal", value: null }
1960
1996
  };
1961
1997
  }
1962
- function replacePlanReferences(block, replacements) {
1963
- const rewrite = (expression) => {
1964
- if (expression.kind === "column") {
1965
- const replacement = replacements.get(expression.reference);
1966
- return replacement == null ? expression : structuredClone(replacement);
1998
+ function replaceExpressionReferences(expression, replacements) {
1999
+ const rewrite = (expression2) => {
2000
+ if (expression2.kind === "column") {
2001
+ const replacement = replacements.get(expression2.reference);
2002
+ return replacement == null ? expression2 : structuredClone(replacement);
1967
2003
  }
1968
- if (expression.kind === "subquery" || expression.kind === "exists") {
1969
- replacePlanReferences(expression.block, replacements);
1970
- return expression;
2004
+ if (expression2.kind === "subquery" || expression2.kind === "exists") {
2005
+ replacePlanReferences(expression2.block, replacements);
2006
+ return expression2;
1971
2007
  }
1972
- if (expression.kind === "window") {
1973
- expression.partitionBy = expression.partitionBy.map(rewrite);
1974
- for (const order of expression.orderBy)
2008
+ if (expression2.kind === "window") {
2009
+ expression2.partitionBy = expression2.partitionBy.map(rewrite);
2010
+ for (const order of expression2.orderBy)
1975
2011
  order.expression = rewrite(order.expression);
1976
- if (expression.argument !== void 0)
1977
- expression.argument = rewrite(expression.argument);
1978
- return expression;
2012
+ if (expression2.argument !== void 0)
2013
+ expression2.argument = rewrite(expression2.argument);
2014
+ return expression2;
1979
2015
  }
1980
- return mapChildExpressions(expression, rewrite);
2016
+ return mapChildExpressions(expression2, rewrite);
1981
2017
  };
1982
- mapBlockExpressions(block, rewrite);
2018
+ return rewrite(expression);
2019
+ }
2020
+ function replacePlanReferences(block, replacements) {
2021
+ mapBlockExpressions(block, (expression) => replaceExpressionReferences(expression, replacements));
1983
2022
  forEachNestedBlock(block, (nested) => replacePlanReferences(nested, replacements));
1984
2023
  }
1985
2024
  function outerProbeBlock(block, keys, resolvedNames) {
1986
2025
  return outerProbeBlockForExpressions(block, keys.map((key) => key.outer), resolvedNames);
1987
2026
  }
1988
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) {
1989
2056
  const sources = [block.base, ...block.joins];
1990
2057
  const sourceIndexes = outerExpressions.flatMap((expression) => {
1991
2058
  const aliases = [...expressionAliases(expression)];
@@ -1999,22 +2066,9 @@ function outerProbeBlockForExpressions(block, outerExpressions, resolvedNames) {
1999
2066
  });
2000
2067
  });
2001
2068
  const lastSource = Math.max(...sourceIndexes);
2002
- const expressions = outerExpressions.map((expression) => structuredClone(expression));
2003
2069
  const retained = new Set(sources.slice(0, lastSource + 1).map((source) => source.alias));
2004
2070
  const retainsAll = resolvedNames && lastSource === sources.length - 1;
2005
- return {
2006
- sql: "(correlation probes)",
2007
- base: structuredClone(block.base),
2008
- joins: structuredClone(block.joins.slice(0, lastSource)),
2009
- select: expressions.map((expression, index) => ({
2010
- expression,
2011
- alias: correlationProbeAlias(index)
2012
- })),
2013
- predicates: probePredicates(block, retained, retainsAll),
2014
- groupBy: expressions,
2015
- having: [],
2016
- orderBy: []
2017
- };
2071
+ return { lastSource, retained, retainsAll };
2018
2072
  }
2019
2073
  function probePredicates(block, retained, retainsAll) {
2020
2074
  return block.predicates.filter((predicate) => probeSafeExpression(predicate.left, retained, retainsAll) && probeSafeExpression(predicate.right, retained, retainsAll)).map((predicate) => structuredClone(predicate));
@@ -2166,30 +2220,62 @@ function blockReferencesOutside(block) {
2166
2220
  collectOutsideReferences(block, /* @__PURE__ */ new Set(), refs);
2167
2221
  return refs.length > 0;
2168
2222
  }
2169
- function collectOutsideReferences(block, outer, refs) {
2170
- const scope = /* @__PURE__ */ new Set([...outer, block.base.alias, ...block.joins.map((join) => join.alias)]);
2171
- const visit = (expression) => {
2172
- if (expression.kind === "column") {
2173
- const parts = expression.reference.split(".");
2223
+ function collectExpressionOutsideReferences(expression, scope, refs) {
2224
+ const visit = (expression2) => {
2225
+ if (expression2.kind === "column") {
2226
+ const parts = expression2.reference.split(".");
2174
2227
  const alias = parts[0];
2175
2228
  if (parts.length === 2 && alias !== void 0 && !scope.has(alias)) {
2176
- refs.push(expression.reference);
2229
+ refs.push(expression2.reference);
2177
2230
  }
2178
2231
  return;
2179
2232
  }
2180
- if (expression.kind === "subquery" || expression.kind === "exists") {
2181
- collectOutsideReferences(expression.block, scope, refs);
2233
+ if (expression2.kind === "subquery" || expression2.kind === "exists") {
2234
+ collectOutsideReferences(expression2.block, scope, refs);
2182
2235
  return;
2183
2236
  }
2184
- for (const child of childExpressions(expression))
2237
+ for (const child of childExpressions(expression2))
2185
2238
  visit(child);
2186
2239
  };
2187
- forEachBlockExpression(block, visit);
2240
+ visit(expression);
2241
+ }
2242
+ function collectOutsideReferences(block, outer, refs) {
2243
+ const scope = /* @__PURE__ */ new Set([...outer, block.base.alias, ...block.joins.map((join) => join.alias)]);
2244
+ forEachBlockExpression(block, (expression) => collectExpressionOutsideReferences(expression, scope, refs));
2188
2245
  forEachNestedBlock(block, (nested) => {
2189
2246
  collectOutsideReferences(nested, scope, refs);
2190
2247
  });
2191
2248
  }
2249
+ function probeOuterReferences(block, additional, scope, label) {
2250
+ const outside = [];
2251
+ collectOutsideReferences(block, /* @__PURE__ */ new Set(), outside);
2252
+ const references = [.../* @__PURE__ */ new Set([...outside, ...additional])];
2253
+ if (references.length === 0) {
2254
+ throw new TypeError(`Correlated ${label} subquery has no usable outer reference`);
2255
+ }
2256
+ for (const reference of references) {
2257
+ const parts = reference.split(".");
2258
+ const alias = parts.length === 2 ? parts[0] : void 0;
2259
+ if (alias === void 0 || !scope.has(alias)) {
2260
+ throw new TypeError(`Correlated ${label} references an unavailable outer source: ${reference}`);
2261
+ }
2262
+ }
2263
+ return references;
2264
+ }
2192
2265
  function extractCorrelation(inner, outerScope, label, comparisons = false) {
2266
+ const keys = takeCorrelationPredicates(inner, outerScope, comparisons);
2267
+ const leftover = [];
2268
+ collectOutsideReferences(inner, /* @__PURE__ */ new Set(), leftover);
2269
+ if (leftover.length > 0) {
2270
+ const supported = comparisons ? "comparison" : "equality";
2271
+ throw new TypeError(`Correlated ${label} subqueries support only ${supported} conditions between one inner and one outer qualified column (unsupported reference: ${leftover[0] ?? ""})`);
2272
+ }
2273
+ if (keys.length === 0) {
2274
+ throw new TypeError(`Correlated ${label} subquery has no usable correlation condition`);
2275
+ }
2276
+ return keys;
2277
+ }
2278
+ function takeCorrelationPredicates(inner, outerScope, comparisons) {
2193
2279
  const innerScope = /* @__PURE__ */ new Set([inner.base.alias, ...inner.joins.map((join) => join.alias)]);
2194
2280
  const keys = [];
2195
2281
  const kept = [];
@@ -2201,15 +2287,6 @@ function extractCorrelation(inner, outerScope, label, comparisons = false) {
2201
2287
  keys.push(key);
2202
2288
  }
2203
2289
  inner.predicates = kept;
2204
- const leftover = [];
2205
- collectOutsideReferences(inner, /* @__PURE__ */ new Set(), leftover);
2206
- if (leftover.length > 0) {
2207
- const supported = comparisons ? "comparison" : "equality";
2208
- throw new TypeError(`Correlated ${label} subqueries support only ${supported} conditions between one inner and one outer qualified column (unsupported reference: ${leftover[0] ?? ""})`);
2209
- }
2210
- if (keys.length === 0) {
2211
- throw new TypeError(`Correlated ${label} subquery has no usable correlation condition`);
2212
- }
2213
2290
  return keys;
2214
2291
  }
2215
2292
  function canExtractCorrelation(inner, outerScope, label, comparisons) {
@@ -2328,14 +2405,15 @@ function foldExpression(expression) {
2328
2405
  const left = foldExpression(expression.left);
2329
2406
  const right = foldExpression(expression.right);
2330
2407
  if (left.kind === "literal" && right.kind === "literal") {
2331
- const folded = foldBinary(expression.operator, left.value, right.value);
2408
+ const folded = foldBinary(expression.operator, left.value, right.value, expression.integer === true);
2332
2409
  if (folded !== void 0) {
2333
2410
  const domain = left.sqlDomain?.kind === "numeric" || right.sqlDomain?.kind === "numeric" ? { kind: "numeric" } : void 0;
2334
2411
  return {
2335
2412
  kind: "literal",
2336
2413
  value: folded,
2337
2414
  ...isSqlDomainValue(folded) ? { internalSqlValue: true } : {},
2338
- ...domain === void 0 ? {} : { sqlDomain: domain }
2415
+ ...domain === void 0 ? {} : { sqlDomain: domain },
2416
+ ...left.decimal === true || right.decimal === true ? { decimal: true } : {}
2339
2417
  };
2340
2418
  }
2341
2419
  }
@@ -2407,7 +2485,7 @@ function foldExpression(expression) {
2407
2485
  }
2408
2486
  return expression;
2409
2487
  }
2410
- function foldBinary(operator, leftValue, rightValue) {
2488
+ function foldBinary(operator, leftValue, rightValue, integer = false) {
2411
2489
  if (leftValue === null || rightValue === null)
2412
2490
  return null;
2413
2491
  if (operator === "||") {
@@ -2428,6 +2506,9 @@ function foldBinary(operator, leftValue, rightValue) {
2428
2506
  const right = rightValue instanceof Date ? dateMilliseconds(rightValue) : rightValue;
2429
2507
  if ((operator === "/" || operator === "%") && right === 0)
2430
2508
  return null;
2509
+ if (operator === "/" && integer && typeof left === "number" && typeof right === "number") {
2510
+ return integerQuotient(left, right);
2511
+ }
2431
2512
  const result = operator === "+" ? left + right : operator === "-" ? left - right : operator === "*" ? left * right : operator === "%" ? left % right : left / right;
2432
2513
  return Number.isFinite(result) ? result : void 0;
2433
2514
  }
@@ -2449,7 +2530,7 @@ function pushPredicatesIntoDerived(block) {
2449
2530
  function pushdownTarget(predicate, sources, singleSource) {
2450
2531
  for (const source of sources) {
2451
2532
  const derived = source.derived;
2452
- if (derived === void 0 || derived.limit !== void 0)
2533
+ if (derived === void 0 || blockHasRowWindow(derived))
2453
2534
  continue;
2454
2535
  const left = rewriteForInner(predicate.left, source, derived, singleSource);
2455
2536
  if (left === void 0)
@@ -2681,7 +2762,7 @@ function referencedOutputAliases(block, source, singleSource) {
2681
2762
  }
2682
2763
  function combineDerivedLimit(block) {
2683
2764
  const derived = block.base.derived;
2684
- 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) {
2685
2766
  return;
2686
2767
  }
2687
2768
  derived.limit = Math.min(derived.limit ?? Number.MAX_SAFE_INTEGER, block.limit);
@@ -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