@rex0220/kintone-sql-tools 3.64.0 → 3.66.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/dist-cli/ksql.js CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/cli/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ CLI_HELP_TEXT: () => CLI_HELP_TEXT,
24
25
  CLI_IMPORT_SOURCE_REQUIRED_MESSAGE: () => CLI_IMPORT_SOURCE_REQUIRED_MESSAGE,
25
26
  HELP_TEXT: () => HELP_TEXT,
26
27
  buildBatchDmlConfirmMessage: () => buildBatchDmlConfirmMessage,
@@ -1085,6 +1086,12 @@ var Parser = class {
1085
1086
  this.tempTableRefs = [];
1086
1087
  /** GROUP BY を読む前に作る B124 候補 leaf の診断位置。AST 公開型へ位置情報を足さない。 */
1087
1088
  this.aggregateGroupKeyTokens = /* @__PURE__ */ new WeakMap();
1089
+ // ----------------------------------------------------------
1090
+ // WITH 句(CTE)
1091
+ // ----------------------------------------------------------
1092
+ /** B53 parser-private state. Kept here so earlier diagnostic source locations remain stable. */
1093
+ this.activeCteDefinition = null;
1094
+ this.provisionalRecursiveCte = null;
1088
1095
  this.allowSelectArithVariable = false;
1089
1096
  }
1090
1097
  // ----------------------------------------------------------
@@ -1919,34 +1926,172 @@ var Parser = class {
1919
1926
  offset
1920
1927
  };
1921
1928
  }
1922
- // ----------------------------------------------------------
1923
- // WITH 句(CTE)
1924
- // ----------------------------------------------------------
1925
1929
  parseWith() {
1926
1930
  this.expect("WITH" /* WITH */);
1931
+ const recursive = this.isSoftKeyword("RECURSIVE") && this.peekAt(1).kind !== "AS" /* AS */ && this.peekAt(1).kind !== "(" /* LPAREN */;
1932
+ if (recursive) this.advance();
1927
1933
  const ctes = [];
1928
- do {
1929
- const name = this.parseIdentifier();
1930
- this.expect("AS" /* AS */);
1931
- this.expect("(" /* LPAREN */);
1932
- let query2;
1933
- const inner = this.peek().kind;
1934
- if (inner === "SHOW" /* SHOW */) {
1935
- query2 = this.parseShow();
1936
- } else if (inner === "DESCRIBE" /* DESCRIBE */ || inner === "DESC" /* DESC */) {
1937
- query2 = this.parseDescribe();
1938
- } else if (inner === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "GENERATE_SERIES") {
1939
- query2 = this.parseGenerateSeries();
1940
- } else {
1941
- query2 = this.tryParseUnionChain(this.parseSelect());
1934
+ let recursiveCount = 0;
1935
+ try {
1936
+ do {
1937
+ const name = this.parseIdentifier();
1938
+ const columnAliases = this.parseOptionalCteColumnAliases();
1939
+ this.expect("AS" /* AS */);
1940
+ this.expect("(" /* LPAREN */);
1941
+ this.activeCteDefinition = { name, recursiveWith: recursive, phase: "SEED" };
1942
+ let query2;
1943
+ let recursiveSpec;
1944
+ const inner = this.peek().kind;
1945
+ if (inner === "SHOW" /* SHOW */) {
1946
+ query2 = this.parseShow();
1947
+ } else if (inner === "DESCRIBE" /* DESCRIBE */ || inner === "DESC" /* DESC */) {
1948
+ query2 = this.parseDescribe();
1949
+ } else if (inner === "IDENT" /* IDENT */ && this.peek().value.toUpperCase() === "GENERATE_SERIES") {
1950
+ query2 = this.parseGenerateSeries();
1951
+ } else if (recursive) {
1952
+ const parsed = this.parseRecursiveCteCandidate(name);
1953
+ query2 = parsed.query;
1954
+ recursiveSpec = parsed.recursiveSpec;
1955
+ } else {
1956
+ query2 = this.tryParseUnionChain(this.parseSelect());
1957
+ }
1958
+ this.expect(")" /* RPAREN */);
1959
+ const cycle = recursive && this.isSoftKeyword("CYCLE") ? this.parseRecursiveCycleClause() : null;
1960
+ if (cycle && !recursiveSpec) {
1961
+ throw new ParseError("CYCLE \u53E5\u306F\u81EA\u5DF1\u53C2\u7167\u3059\u308B\u518D\u5E30 CTE \u306B\u3060\u3051\u6307\u5B9A\u3067\u304D\u307E\u3059", this.prev());
1962
+ }
1963
+ if (recursiveSpec) {
1964
+ recursiveSpec = { ...recursiveSpec, cycle };
1965
+ this.validateRecursiveCte(name, columnAliases, recursiveSpec);
1966
+ recursiveCount++;
1967
+ if (recursiveCount > 1) {
1968
+ throw new ParseError("WITH RECURSIVE \u3067\u5B9A\u7FA9\u3067\u304D\u308B\u518D\u5E30 CTE \u306F1\u500B\u307E\u3067\u3067\u3059", this.prev());
1969
+ }
1970
+ }
1971
+ if (columnAliases && !recursiveSpec) {
1972
+ throw new ParseError("CTE \u306E\u5217\u540D\u30EA\u30B9\u30C8\u306F WITH RECURSIVE \u306E\u518D\u5E30 CTE \u306B\u3060\u3051\u6307\u5B9A\u3067\u304D\u307E\u3059", this.prev());
1973
+ }
1974
+ const definition = { name, query: query2 };
1975
+ if (columnAliases) definition.columnAliases = columnAliases;
1976
+ if (recursiveSpec) definition.recursiveSpec = recursiveSpec;
1977
+ ctes.push(definition);
1978
+ this.activeCteDefinition = null;
1979
+ this.provisionalRecursiveCte = null;
1980
+ this.cteNames.add(name);
1981
+ } while (this.consume("," /* COMMA */));
1982
+ const query = this.tryParseUnionChain(this.parseSelect());
1983
+ return recursive ? { type: "WITH", ctes, query, recursive: true } : { type: "WITH", ctes, query };
1984
+ } finally {
1985
+ this.activeCteDefinition = null;
1986
+ this.provisionalRecursiveCte = null;
1987
+ this.cteNames.clear();
1988
+ }
1989
+ }
1990
+ parseOptionalCteColumnAliases() {
1991
+ if (!this.consume("(" /* LPAREN */)) return void 0;
1992
+ const aliases = [];
1993
+ if (this.peek().kind === ")" /* RPAREN */) {
1994
+ throw new ParseError("CTE \u306E\u5217\u540D\u30EA\u30B9\u30C8\u306B\u306F1\u500B\u4EE5\u4E0A\u306E\u5217\u540D\u304C\u5FC5\u8981\u3067\u3059", this.peek());
1995
+ }
1996
+ do
1997
+ aliases.push(this.parseIdentifier());
1998
+ while (this.consume("," /* COMMA */));
1999
+ this.expect(")" /* RPAREN */);
2000
+ if (new Set(aliases).size !== aliases.length) {
2001
+ throw new ParseError("CTE \u306E\u5217\u540D\u30EA\u30B9\u30C8\u306B\u540C\u3058\u5217\u540D\u3092\u91CD\u8907\u3057\u3066\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093", this.prev());
2002
+ }
2003
+ return aliases;
2004
+ }
2005
+ parseRecursiveCteCandidate(name) {
2006
+ const seed = this.parseSelect();
2007
+ if (this.peek().kind !== "UNION" /* UNION */) return { query: seed };
2008
+ this.advance();
2009
+ const all = this.consume("ALL" /* ALL */);
2010
+ this.activeCteDefinition = { name, recursiveWith: true, phase: "RECURSIVE_TERM" };
2011
+ this.provisionalRecursiveCte = { name, references: 0 };
2012
+ const recursiveTerm = this.parseSelect();
2013
+ const references = this.provisionalRecursiveCte.references;
2014
+ this.provisionalRecursiveCte = null;
2015
+ let query = { type: "UNION", all, left: seed, right: recursiveTerm };
2016
+ query = this.tryParseUnionChain(query);
2017
+ this.activeCteDefinition = { name, recursiveWith: true, phase: "SEED" };
2018
+ if (references === 0) return { query };
2019
+ if (!all || query.type !== "UNION" || query.left !== seed || query.right !== recursiveTerm) {
2020
+ throw new ParseError("\u518D\u5E30 CTE \u306F seed SELECT UNION ALL recursive SELECT \u306E2\u5206\u5C90\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
2021
+ }
2022
+ if (references !== 1) {
2023
+ throw new ParseError("\u518D\u5E30\u9805\u304B\u3089\u306E\u81EA\u5DF1\u53C2\u7167\u306F\u3061\u3087\u3046\u30691\u56DE\u306B\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
2024
+ }
2025
+ return {
2026
+ query,
2027
+ recursiveSpec: { seed, recursiveTerm, unionAll: true, cycle: null }
2028
+ };
2029
+ }
2030
+ parseRecursiveCycleClause() {
2031
+ this.advance();
2032
+ const column = this.parseIdentifier();
2033
+ this.expect("SET" /* SET */, "CYCLE \u306E\u5217\u540D\u306E\u5F8C\u306B\u306F SET \u304C\u5FC5\u8981\u3067\u3059");
2034
+ const markColumn = this.parseIdentifier();
2035
+ if (!this.isSoftKeyword("TO")) throw new ParseError("CYCLE \u306E mark \u5217\u306E\u5F8C\u306B\u306F TO \u304C\u5FC5\u8981\u3067\u3059", this.peek());
2036
+ this.advance();
2037
+ const mark = this.expect("STRING" /* STRING */, "CYCLE TO \u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
2038
+ if (!this.isSoftKeyword("DEFAULT")) throw new ParseError("CYCLE TO \u306E\u5024\u306E\u5F8C\u306B\u306F DEFAULT \u304C\u5FC5\u8981\u3067\u3059", this.peek());
2039
+ this.advance();
2040
+ const normal = this.expect("STRING" /* STRING */, "CYCLE DEFAULT \u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
2041
+ if (mark.value === normal.value) {
2042
+ throw new ParseError("CYCLE \u306E TO \u3068 DEFAULT \u306B\u306F\u7570\u306A\u308B\u6587\u5B57\u5217\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", normal);
2043
+ }
2044
+ return { column, markColumn, markValue: mark.value, defaultValue: normal.value, exposePath: false };
2045
+ }
2046
+ validateRecursiveCte(name, columnAliases, spec) {
2047
+ const token = this.prev();
2048
+ const seed = spec.seed;
2049
+ const term = spec.recursiveTerm;
2050
+ const { groupBy } = term;
2051
+ if (seed.columns.length !== term.columns.length) {
2052
+ throw new ParseError("\u518D\u5E30 CTE \u306E seed \u3068\u518D\u5E30\u9805\u306E\u5217\u6570\u3092\u4E00\u81F4\u3055\u305B\u3066\u304F\u3060\u3055\u3044", token);
2053
+ }
2054
+ if (columnAliases && columnAliases.length !== seed.columns.length) {
2055
+ throw new ParseError("CTE \u306E\u5217\u540D\u30EA\u30B9\u30C8\u3068 SELECT \u306E\u5217\u6570\u3092\u4E00\u81F4\u3055\u305B\u3066\u304F\u3060\u3055\u3044", token);
2056
+ }
2057
+ if (seed.columns.some((column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD") || term.columns.some((column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD")) {
2058
+ throw new ParseError("\u518D\u5E30 CTE \u306E seed \u3068\u518D\u5E30\u9805\u3067\u306F\u5217\u3092\u660E\u793A\u7684\u306B\u5C04\u5F71\u3057\u3066\u304F\u3060\u3055\u3044", token);
2059
+ }
2060
+ if (term.distinct || groupBy.length > 0 || term.grouping !== void 0 || term.having !== null || term.orderBy.length > 0 || term.orderMode !== "CANONICAL" || term.limit !== null || term.offset !== null) {
2061
+ throw new ParseError("\u518D\u5E30\u9805\u3067\u306F DISTINCT\u3001\u96C6\u8A08\u3001window\u3001GROUP BY\u3001HAVING\u3001ORDER BY\u3001LIMIT\u3001OFFSET \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", token);
2062
+ }
2063
+ if (term.joins.length !== 1 || term.joins[0].type !== "INNER") {
2064
+ throw new ParseError("\u518D\u5E30\u9805\u306F\u81EA\u5DF1\u53C2\u7167\u3068\u7269\u7406 source \u307E\u305F\u306F\u5148\u884C CTE \u306E INNER JOIN 1\u500B\u3067\u69CB\u6210\u3057\u3066\u304F\u3060\u3055\u3044", token);
2065
+ }
2066
+ const relationNames = [term.from.cteName, term.joins[0].table.cteName];
2067
+ if (relationNames.filter((value) => value === name).length !== 1) {
2068
+ throw new ParseError("\u518D\u5E30\u9805\u306E INNER JOIN \u306B\u306F\u81EA\u5DF1\u53C2\u7167\u3092\u3061\u3087\u3046\u30691\u56DE\u542B\u3081\u3066\u304F\u3060\u3055\u3044", token);
2069
+ }
2070
+ if (this.containsRecursiveForbiddenNode(term.columns) || this.containsRecursiveForbiddenNode(term.where)) {
2071
+ throw new ParseError("\u518D\u5E30\u9805\u3067\u306F\u96C6\u8A08\u3001window\u3001DISTINCT\u3001subquery \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", token);
2072
+ }
2073
+ const outputNames = columnAliases ?? seed.columns.map((column) => this.selectColumnOutputName(column));
2074
+ if (spec.cycle) {
2075
+ const cycleMatches = outputNames.filter((value) => value === spec.cycle.column).length;
2076
+ if (cycleMatches !== 1) {
2077
+ throw new ParseError("CYCLE \u5217\u306F\u518D\u5E30 CTE \u306E\u51FA\u529B\u52171\u500B\u3078\u4E00\u610F\u306B\u89E3\u6C7A\u3067\u304D\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059", token);
1942
2078
  }
1943
- this.expect(")" /* RPAREN */);
1944
- ctes.push({ name, query: query2 });
1945
- this.cteNames.add(name);
1946
- } while (this.consume("," /* COMMA */));
1947
- const query = this.tryParseUnionChain(this.parseSelect());
1948
- this.cteNames.clear();
1949
- return { type: "WITH", ctes, query };
2079
+ if (outputNames.includes(spec.cycle.markColumn)) {
2080
+ throw new ParseError("CYCLE \u306E mark \u5217\u306F\u518D\u5E30 CTE \u306E\u65E2\u5B58\u51FA\u529B\u5217\u3068\u540C\u540D\u306B\u3067\u304D\u307E\u305B\u3093", token);
2081
+ }
2082
+ }
2083
+ }
2084
+ containsRecursiveForbiddenNode(value) {
2085
+ if (Array.isArray(value)) return value.some((item) => this.containsRecursiveForbiddenNode(item));
2086
+ if (value === null || typeof value !== "object") return false;
2087
+ const node = value;
2088
+ if (node.type === "AGGREGATE" || node.type === "AGG_REF" || node.type === "ARITH_AGG_COL" || node.type === "WINDOW_COL" || node.type === "SCALAR_SUBQUERY" || node.type === "SCALAR_SUBQUERY_COL" || node.type === "SUBQUERY_IN_LIST" || node.type === "EXISTS") return true;
2089
+ return Object.values(node).some((item) => this.containsRecursiveForbiddenNode(item));
2090
+ }
2091
+ selectColumnOutputName(column) {
2092
+ if ("alias" in column && typeof column.alias === "string") return column.alias;
2093
+ if (column.type === "FIELD") return column.field;
2094
+ return null;
1950
2095
  }
1951
2096
  parseGenerateSeries() {
1952
2097
  const name = this.advance();
@@ -2985,6 +3130,15 @@ var Parser = class {
2985
3130
  const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
2986
3131
  return { appId: 0, alias: alias2, cteName: name };
2987
3132
  }
3133
+ if (this.activeCteDefinition?.name === name) {
3134
+ if (this.activeCteDefinition.recursiveWith && this.activeCteDefinition.phase === "RECURSIVE_TERM" && this.provisionalRecursiveCte?.name === name) {
3135
+ this.provisionalRecursiveCte.references++;
3136
+ const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
3137
+ return { appId: 0, alias: alias2, cteName: name };
3138
+ }
3139
+ const message = this.activeCteDefinition.recursiveWith ? this.activeCteDefinition.phase === "RECURSIVE_TERM" ? "\u518D\u5E30 CTE \u306F seed SELECT UNION ALL recursive SELECT \u306E2\u5206\u5C90\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044" : "\u518D\u5E30 CTE \u306E seed \u304B\u3089\u81EA\u5206\u81EA\u8EAB\u3092\u53C2\u7167\u3067\u304D\u307E\u305B\u3093" : "CTE \u306E\u5B9A\u7FA9\u5185\u304B\u3089\u81EA\u5206\u81EA\u8EAB\u3092\u53C2\u7167\u3057\u3066\u3044\u307E\u3059\u3002\u81EA\u5DF1\u53C2\u7167\u306B\u306F `WITH RECURSIVE` \u304C\u5FC5\u8981\u3067\u3059";
3140
+ throw new ParseError(message, nameTok);
3141
+ }
2988
3142
  const { appId, subtableCode } = extractTableRef(name, this.prev());
2989
3143
  if (subtableCode) {
2990
3144
  const alias2 = this.consume("AS" /* AS */) ? this.parseTableAliasName() : this.tryParseImplicitAlias();
@@ -10008,6 +10162,10 @@ function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
10008
10162
  if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
10009
10163
  if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
10010
10164
  if (field.type === "GROUPING_FIELD") return evalGroupingRef(field.ref, row);
10165
+ if (field.aggregateRef) {
10166
+ const ref = field.aggregateRef;
10167
+ return resolveFieldRef(row, aggregateSyntheticName(ref.func, ref.distinct, ref.arg));
10168
+ }
10011
10169
  const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
10012
10170
  return resolveFieldRef(row, key);
10013
10171
  }
@@ -12800,6 +12958,71 @@ function isOuterJoinNonPreservedTable(statement, table, isMainTable) {
12800
12958
  return false;
12801
12959
  }
12802
12960
 
12961
+ // src/core/recursiveCte.ts
12962
+ var RECURSIVE_CTE_MAX_DEPTH = 100;
12963
+ var RECURSIVE_CTE_MAX_ROWS = 1e4;
12964
+ var RECURSIVE_CTE_MAX_EXPANSIONS = 1e5;
12965
+ function resolveRecursiveCteLimits(options) {
12966
+ const entries = [
12967
+ ["recursiveCteMaxDepth", options.recursiveCteMaxDepth, RECURSIVE_CTE_MAX_DEPTH],
12968
+ ["recursiveCteMaxRows", options.recursiveCteMaxRows, RECURSIVE_CTE_MAX_ROWS],
12969
+ ["recursiveCteMaxExpansions", options.recursiveCteMaxExpansions, RECURSIVE_CTE_MAX_EXPANSIONS]
12970
+ ];
12971
+ for (const [name, value] of entries) {
12972
+ if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) {
12973
+ throw new RangeError(`${name} must be a positive safe integer`);
12974
+ }
12975
+ }
12976
+ return {
12977
+ depth: options.recursiveCteMaxDepth ?? RECURSIVE_CTE_MAX_DEPTH,
12978
+ rows: options.recursiveCteMaxRows ?? RECURSIVE_CTE_MAX_ROWS,
12979
+ expansions: options.recursiveCteMaxExpansions ?? RECURSIVE_CTE_MAX_EXPANSIONS
12980
+ };
12981
+ }
12982
+ var RecursiveCteLimitError = class extends Error {
12983
+ constructor(kind, limit, detected, cteName) {
12984
+ const label = kind === "DEPTH" ? "\u6DF1\u3055" : kind === "ROWS" ? "\u7D50\u679C\u884C\u6570" : "\u4E2D\u9593\u5C55\u958B\u6570";
12985
+ const unit = kind === "DEPTH" ? "" : " \u4EF6";
12986
+ super(`\u518D\u5E30 CTE\u300C${cteName}\u300D\u306E${label}\u304C\u4E0A\u9650 ${limit}${unit}\u3092\u8D85\u3048\u307E\u3057\u305F\uFF08\u691C\u51FA\u5024: ${detected}${unit}\uFF09\u3002`);
12987
+ this.name = "RecursiveCteLimitError";
12988
+ this.kind = kind;
12989
+ this.limit = limit;
12990
+ this.detected = detected;
12991
+ this.cteName = cteName;
12992
+ Object.setPrototypeOf(this, new.target.prototype);
12993
+ }
12994
+ };
12995
+ var RecursiveCteLimitCounter = class {
12996
+ constructor(cteName, limits = resolveRecursiveCteLimits({})) {
12997
+ this.cteName = cteName;
12998
+ this.limits = limits;
12999
+ this.rows = 0;
13000
+ this.expansions = 0;
13001
+ }
13002
+ observeDepth(detected) {
13003
+ if (detected > this.limits.depth) {
13004
+ throw new RecursiveCteLimitError("DEPTH", this.limits.depth, detected, this.cteName);
13005
+ }
13006
+ }
13007
+ addRow() {
13008
+ this.rows++;
13009
+ if (this.rows > this.limits.rows) {
13010
+ throw new RecursiveCteLimitError("ROWS", this.limits.rows, this.rows, this.cteName);
13011
+ }
13012
+ }
13013
+ addExpansion() {
13014
+ this.expansions++;
13015
+ if (this.expansions > this.limits.expansions) {
13016
+ throw new RecursiveCteLimitError(
13017
+ "EXPANSIONS",
13018
+ this.limits.expansions,
13019
+ this.expansions,
13020
+ this.cteName
13021
+ );
13022
+ }
13023
+ }
13024
+ };
13025
+
12803
13026
  // src/core/optimization/joinKeyPrefilter.ts
12804
13027
  var JOIN_KEY_IN_CHUNK_SIZE = 50;
12805
13028
  function buildJoinKeyPrefilterQueries(plan, field, quoteValue) {
@@ -14129,6 +14352,7 @@ async function deriveEmptyWildcardColumns(fields, subtableCode, loadProcessStatu
14129
14352
  // src/engine/process.ts
14130
14353
  var materializedSelectValues = /* @__PURE__ */ new WeakMap();
14131
14354
  var sourceRows = /* @__PURE__ */ new WeakMap();
14355
+ var UNRESOLVED_AGGREGATE_COMPARISON_WARNING = "\u6BD4\u8F03\u6761\u4EF6\u3067\u53C2\u7167\u3057\u305F\u96C6\u8A08\u5024\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002SELECT \u30EA\u30B9\u30C8\u306B\u540C\u3058\u96C6\u8A08\u5F0F\u3092\u542B\u3081\u3066\u304F\u3060\u3055\u3044\u3002";
14132
14356
  function asProcessingRow(source) {
14133
14357
  if (sourceRows.has(source)) return source;
14134
14358
  let row;
@@ -14177,6 +14401,31 @@ function getMaterializedLookupValue(row, key) {
14177
14401
  function getLegacyMaterializedValue(row, key) {
14178
14402
  return materializedSelectValues.has(row) ? void 0 : row[key];
14179
14403
  }
14404
+ function warnOnUnresolvedAggregateComparisons(node, rows, warnings) {
14405
+ if (!warnings || rows.length === 0) return;
14406
+ const keys = /* @__PURE__ */ new Set();
14407
+ const visit = (value) => {
14408
+ if (value === null || typeof value !== "object") return;
14409
+ if (Array.isArray(value)) {
14410
+ value.forEach(visit);
14411
+ return;
14412
+ }
14413
+ const record = value;
14414
+ if (record["type"] === "FIELD" && record["aggregateRef"] !== void 0) {
14415
+ const ref = record["aggregateRef"];
14416
+ keys.add(aggregateSyntheticName(ref.func, ref.distinct, ref.arg));
14417
+ return;
14418
+ }
14419
+ if (record["type"] === "SELECT" || record["type"] === "SCALAR_SUBQUERY") return;
14420
+ Object.values(record).forEach(visit);
14421
+ };
14422
+ visit(node);
14423
+ if ([...keys].some((key) => rows.some(
14424
+ (row) => getMaterializedLookupValue(row, key) === void 0 && row[key] === void 0
14425
+ ))) {
14426
+ warnings.add(UNRESOLVED_AGGREGATE_COMPARISON_WARNING);
14427
+ }
14428
+ }
14180
14429
  function havingEvaluationRow(row) {
14181
14430
  const lookups = materializedSelectValues.get(row)?.byLookupKey;
14182
14431
  if (!lookups || lookups.size === 0) return row;
@@ -14215,8 +14464,15 @@ function applyJoin(leftRows, rightRows, join2, columns = {}) {
14215
14464
  return result2;
14216
14465
  }
14217
14466
  const { on, type: joinType } = join2;
14218
- const leftKey = on.left.tableAlias ? `${on.left.tableAlias}.${on.left.field}` : on.left.field;
14219
- const rightKey = on.right.tableAlias ? `${on.right.tableAlias}.${on.right.field}` : on.right.field;
14467
+ const normalizedOn = normalizeJoinConditionSides(
14468
+ on,
14469
+ leftRows,
14470
+ rightRows,
14471
+ columns.leftColumns,
14472
+ columns.rightColumns
14473
+ );
14474
+ const leftKey = joinFieldKey(normalizedOn.left);
14475
+ const rightKey = joinFieldKey(normalizedOn.right);
14220
14476
  assertJoinKeyAvailable(leftRows, leftKey, columns.leftColumns);
14221
14477
  assertJoinKeyAvailable(rightRows, rightKey, columns.rightColumns);
14222
14478
  if (joinType === "RIGHT") {
@@ -14264,6 +14520,37 @@ function applyJoin(leftRows, rightRows, join2, columns = {}) {
14264
14520
  }
14265
14521
  return result;
14266
14522
  }
14523
+ function joinFieldKey(ref) {
14524
+ return ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
14525
+ }
14526
+ function normalizeJoinConditionSides(on, leftRows, rightRows, leftColumns, rightColumns) {
14527
+ const leftSchema = leftColumns ?? Object.keys(leftRows[0] ?? {});
14528
+ const rightSchema = rightColumns ?? Object.keys(rightRows[0] ?? {});
14529
+ const leftAliases = qualifiedAliases(leftSchema);
14530
+ const rightAliases = qualifiedAliases(rightSchema);
14531
+ const sideFor = (ref) => {
14532
+ if (ref.tableAlias) {
14533
+ const inLeft2 = leftAliases.has(ref.tableAlias);
14534
+ const inRight2 = rightAliases.has(ref.tableAlias);
14535
+ if (inLeft2 !== inRight2) return inLeft2 ? "LEFT" : "RIGHT";
14536
+ }
14537
+ const key = joinFieldKey(ref);
14538
+ const inLeft = leftSchema.includes(key);
14539
+ const inRight = rightSchema.includes(key);
14540
+ return inLeft !== inRight ? inLeft ? "LEFT" : "RIGHT" : "UNKNOWN";
14541
+ };
14542
+ const leftSide = sideFor(on.left);
14543
+ const rightSide = sideFor(on.right);
14544
+ return leftSide === "RIGHT" && rightSide === "LEFT" ? { left: on.right, right: on.left } : on;
14545
+ }
14546
+ function qualifiedAliases(columns) {
14547
+ const aliases = /* @__PURE__ */ new Set();
14548
+ for (const column of columns) {
14549
+ const separator = column.indexOf(".");
14550
+ if (separator > 0) aliases.add(column.slice(0, separator));
14551
+ }
14552
+ return aliases;
14553
+ }
14267
14554
  function assertJoinKeyAvailable(rows, key, savedColumns) {
14268
14555
  const missing = rows.length > 0 ? rows.some((row) => !Object.prototype.hasOwnProperty.call(row, key)) : savedColumns !== void 0 && !savedColumns.includes(key);
14269
14556
  if (missing) {
@@ -15481,7 +15768,8 @@ function runFullScan(input) {
15481
15768
  tableColumns,
15482
15769
  hiddenQualifiedAliases,
15483
15770
  resolvedGroupingSpec,
15484
- plainGroupByPlan
15771
+ plainGroupByPlan,
15772
+ warnings
15485
15773
  } = input;
15486
15774
  const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns, aggregateSortKindResolver);
15487
15775
  for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
@@ -15537,7 +15825,9 @@ function runFullScan(input) {
15537
15825
  }
15538
15826
  );
15539
15827
  }
15828
+ warnOnUnresolvedAggregateComparisons(stmt.columns, rows, warnings);
15540
15829
  const resolveHavingSemantics = (field) => field.aggregateRef ? aggregateResultSemantics(field.aggregateRef, aggregateSortKindResolver) : havingFieldSemanticsResolver?.(field);
15830
+ warnOnUnresolvedAggregateComparisons(stmt.having, rows, warnings);
15541
15831
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, resolveHavingSemantics);
15542
15832
  rows = applyWindow(
15543
15833
  rows,
@@ -17298,6 +17588,7 @@ function createInvocationCacheContext(cacheContext) {
17298
17588
  return `${cacheContext}\0inv:${nextCacheInvocationId++}`;
17299
17589
  }
17300
17590
  async function execute(sql, client, options = {}) {
17591
+ resolveRecursiveCteLimits(options);
17301
17592
  const startedAt = Date.now();
17302
17593
  const cacheContext = createInvocationCacheContext(
17303
17594
  resolveCacheContext(client, options.cacheContext)
@@ -17609,7 +17900,10 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
17609
17900
  options.cursorMaxActive ?? 2,
17610
17901
  stmt.query.type === "UPDATE" && stmt.query.applyBlocks?.length ? resolveApplyGuardLimit(options.dmlMaxRows, "dmlMaxRows", DEFAULT_APPLY_MAX_ROWS) : DEFAULT_APPLY_MAX_ROWS,
17611
17902
  stmt.query.type === "UPDATE" && stmt.query.applyBlocks?.length ? resolveApplyGuardLimit(options.dmlMaxSubtableRows, "dmlMaxSubtableRows", DEFAULT_APPLY_MAX_SUBTABLE_ROWS) : DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
17612
- relativeDatePlan
17903
+ relativeDatePlan,
17904
+ options.recursiveCteMaxDepth,
17905
+ options.recursiveCteMaxRows,
17906
+ options.recursiveCteMaxExpansions
17613
17907
  );
17614
17908
  // 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
17615
17909
  case "CREATE_TEMP_TABLE":
@@ -17881,6 +18175,7 @@ var BatchTimeoutError = class extends Error {
17881
18175
  }
17882
18176
  };
17883
18177
  async function executeBatch(sql, client, options = {}) {
18178
+ resolveRecursiveCteLimits(options);
17884
18179
  const statements = parseSqlBatch(sql, options.enableImport === true);
17885
18180
  const analysis = analyzeBatch(statements);
17886
18181
  statements.forEach((statement) => assertApplyExecutionScope("phase15b", statement));
@@ -18707,7 +19002,7 @@ function canProveTotalWindowOrder(stmt, orderBy, resolveField2, context, generat
18707
19002
  }
18708
19003
  function tieBreakAdvice(context, kind) {
18709
19004
  if (context !== "DIRECT") {
18710
- return "\u305D\u306E\u8868\u306E\u4E2D\u3067\u4E00\u610F\u306B\u306A\u308B\u5217\uFF08\u5143\u306E\u96C6\u7D04\u306E\u30AD\u30FC\u306A\u3069\uFF09\u3092 ORDER BY \u306B\u542B\u3081\u3066\u304F\u3060\u3055\u3044\u3002\u96C6\u7D04\u7D50\u679C\u306E\u5217\u306F\u4E00\u610F\u3067\u3082\u8A3C\u660E\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u3059\u3067\u306B\u4E00\u610F\u306A\u5834\u5408\u3082\u3053\u306E\u8B66\u544A\u304C\u51FA\u307E\u3059\u3002\u5143\u306E\u96C6\u7D04\u306E\u30AD\u30FC\u3092\u3059\u3079\u3066 ORDER BY \u306B\u542B\u3081\u3066\u3044\u308B\u306A\u3089\u3001\u3053\u306E\u8B66\u544A\u306F\u7121\u8996\u3057\u3066\u69CB\u3044\u307E\u305B\u3093\u3002";
19005
+ return "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u5404\u30D1\u30FC\u30C6\u30A3\u30B7\u30E7\u30F3\u5185\u3067\u3001ORDER BY \u306E\u5024\u306E\u7D44\u304C\u5165\u529B\u884C\u3092\u4E00\u610F\u306B\u8B58\u5225\u3059\u308B\u3068\u30AF\u30A8\u30EA\u69CB\u9020\u307E\u305F\u306F\u4FDD\u8A3C\u6E08\u307F\u306E\u30C7\u30FC\u30BF\u5236\u7D04\u304B\u3089\u78BA\u8A8D\u3067\u304D\u308B\u5834\u5408\u306B\u9650\u308A\u3001\u3053\u306E\u8B66\u544A\u306F\u7121\u8996\u3067\u304D\u307E\u3059\u3002\u5143\u306E\u96C6\u7D04\u30AD\u30FC\u3092\u3059\u3079\u3066 ORDER BY \u306B\u542B\u3080\u5F62\u3084\u3001JOIN \u5F8C\u3082\u540C\u3058\u7CFB\u5217\u5024\u304C\u5404\u30D1\u30FC\u30C6\u30A3\u30B7\u30E7\u30F3\u5185\u3067\u9AD8\u30051\u884C\u3068\u4FDD\u8A3C\u3067\u304D\u308B\u5F62\u304C\u8A72\u5F53\u3057\u307E\u3059\u3002\u751F\u6210\u5217\u3001\u518D\u5E30\u306E\u6DF1\u3055\u5217\u3001\u307E\u305F\u306F $id \u306B\u7531\u6765\u3059\u308B\u5217\u3067\u3042\u308B\u3068\u3044\u3046\u7406\u7531\u3060\u3051\u3067\u306F\u7121\u8996\u3067\u304D\u307E\u305B\u3093\u3002";
18711
19006
  }
18712
19007
  return kind === "RANGE" ? "ORDER BY \u306B\u30EC\u30B3\u30FC\u30C9\u756A\u53F7\u306A\u3069\u306E\u30BF\u30A4\u30D6\u30EC\u30FC\u30AF\u30AD\u30FC\u3092\u8DB3\u3057\u3066\u304F\u3060\u3055\u3044\u3002" : "\u30EC\u30B3\u30FC\u30C9\u756A\u53F7\u7B49\u3092 ORDER BY \u306B\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
18713
19008
  }
@@ -18838,6 +19133,7 @@ async function assertDmlWhereCapability(stmt, client, cacheContext) {
18838
19133
  }
18839
19134
  async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false, forLibraryCapture = false, windowWarningContext = "DIRECT") {
18840
19135
  let result;
19136
+ const subqueryWarnings = /* @__PURE__ */ new Set();
18841
19137
  await validateSelectGroupingPlanning(stmt, client, cacheContext, cteCache);
18842
19138
  if (isNoFromSelect(stmt)) {
18843
19139
  result = executeNoFromSelect(stmt);
@@ -18867,7 +19163,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
18867
19163
  cacheContext,
18868
19164
  cteCache
18869
19165
  );
18870
- await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
19166
+ await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, subqueryWarnings, cteCache);
18871
19167
  const whereCapability = rememberSelectWhereCapability(
18872
19168
  stmt,
18873
19169
  classifyWhereCapability(stmt.where, fieldSemanticsResolver)
@@ -18958,7 +19254,11 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
18958
19254
  await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache, forLibraryCapture)
18959
19255
  );
18960
19256
  }
18961
- return mergeSelectWarnings(result, [...defaultRangeWarnings, ...subtableFieldWarnings]);
19257
+ return mergeSelectWarnings(result, [
19258
+ ...subqueryWarnings,
19259
+ ...defaultRangeWarnings,
19260
+ ...subtableFieldWarnings
19261
+ ]);
18962
19262
  }
18963
19263
  function subtableGroupingAdvice(fieldName, owners) {
18964
19264
  if (owners.length === 1) {
@@ -20426,8 +20726,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
20426
20726
  const warnings = /* @__PURE__ */ new Set();
20427
20727
  const parallel = options.fetchParallel ?? 1;
20428
20728
  await Promise.all([
20429
- resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
20430
- resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
20729
+ resolveSubqueries(stmt.where, client, options, cacheContext, warnings, cteCache),
20730
+ resolveSubqueries(stmt.having, client, options, cacheContext, warnings, cteCache)
20431
20731
  ]);
20432
20732
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
20433
20733
  loadTypedPushdownMeta(stmt, client, cacheContext),
@@ -20461,7 +20761,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
20461
20761
  const mainBoundQuery = boundServerFunctionPlan?.queriesByAlias.get(
20462
20762
  stmt.from.alias
20463
20763
  ) ?? "";
20464
- const scalarCache = await resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
20764
+ const scalarCache = await resolveScalarColumns(
20765
+ stmt.columns,
20766
+ client,
20767
+ options,
20768
+ cacheContext,
20769
+ warnings,
20770
+ cteCache
20771
+ );
20465
20772
  const constantFalse = isConstantFalseWhere(stmt.where);
20466
20773
  const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
20467
20774
  stmt,
@@ -20565,7 +20872,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
20565
20872
  appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
20566
20873
  ...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : boundServerFunctionPlan ? { residualWhere: boundServerFunctionPlan.joinPlan.residualWhere } : {},
20567
20874
  resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt),
20568
- plainGroupByPlan
20875
+ plainGroupByPlan,
20876
+ warnings
20569
20877
  });
20570
20878
  const columns = await restoreEmptyWildcardColumns(
20571
20879
  stmt,
@@ -20583,6 +20891,53 @@ function assertUnionColumnCount(leftColumns, rightColumns) {
20583
20891
  UNION \u306F\u5217\u3092\u4F4D\u7F6E\u3067\u5BFE\u5FDC\u3055\u305B\u308B\u305F\u3081\u3001\u4E21\u8FBA\u306E\u5217\u6570\u3092\u63C3\u3048\u3066\u304F\u3060\u3055\u3044`
20584
20892
  );
20585
20893
  }
20894
+ function alignSelectResultByPosition(result, targetColumns, targetMeta) {
20895
+ assertUnionColumnCount(targetColumns, result.columns);
20896
+ const rows = result.rows.map((row) => {
20897
+ const mapped = {};
20898
+ targetColumns.forEach((column, index) => {
20899
+ mapped[column] = row[result.columns[index] ?? column] ?? "";
20900
+ });
20901
+ return mapped;
20902
+ });
20903
+ const aligned = {
20904
+ ...result,
20905
+ rows,
20906
+ columns: [...targetColumns],
20907
+ rowCount: rows.length
20908
+ };
20909
+ const sourceMeta = materializedMetaBySelectResult.get(result);
20910
+ if (targetMeta || sourceMeta) {
20911
+ const meta = /* @__PURE__ */ new Map();
20912
+ targetColumns.forEach((column, index) => {
20913
+ const sourceColumn = result.columns[index];
20914
+ const inferred = targetMeta?.get(column) ?? (sourceColumn === void 0 ? void 0 : sourceMeta?.get(sourceColumn));
20915
+ if (inferred) meta.set(column, { ...inferred, displayName: column });
20916
+ });
20917
+ materializedMetaBySelectResult.set(aligned, meta);
20918
+ }
20919
+ return aligned;
20920
+ }
20921
+ function combineUnionResults(leftResult, rightResult, all, captureColumnMeta) {
20922
+ const alignedRight = alignSelectResultByPosition(rightResult, leftResult.columns);
20923
+ const combined = [...leftResult.rows, ...alignedRight.rows];
20924
+ const rows = all ? combined : deduplicateRows(combined, leftResult.columns);
20925
+ const warnings = [.../* @__PURE__ */ new Set([
20926
+ ...leftResult.warnings ?? [],
20927
+ ...rightResult.warnings ?? []
20928
+ ])];
20929
+ const result = {
20930
+ type: "SELECT",
20931
+ rows,
20932
+ columns: [...leftResult.columns],
20933
+ rowCount: rows.length,
20934
+ warnings
20935
+ };
20936
+ if (captureColumnMeta) {
20937
+ materializedMetaBySelectResult.set(result, mergeUnionColumnMeta(leftResult, rightResult));
20938
+ }
20939
+ return result;
20940
+ }
20586
20941
  async function executeUnion(stmt, client, options, cacheContext, captureColumnMeta = false, forLibraryCapture = false) {
20587
20942
  const completePolicy = buildCompleteInputPolicy(stmt, options, null);
20588
20943
  return withCompleteInputPolicy(completePolicy, async () => {
@@ -20616,33 +20971,7 @@ async function executeUnion(stmt, client, options, cacheContext, captureColumnMe
20616
20971
  "DERIVED"
20617
20972
  )
20618
20973
  ]);
20619
- const leftCols = leftResult.columns;
20620
- const rightCols = rightResult.columns;
20621
- assertUnionColumnCount(leftCols, rightCols);
20622
- const remappedRight = rightResult.rows.map((row) => {
20623
- const mapped = {};
20624
- leftCols.forEach((col, i) => {
20625
- mapped[col] = row[rightCols[i] ?? col] ?? "";
20626
- });
20627
- return mapped;
20628
- });
20629
- const combined = [...leftResult.rows, ...remappedRight];
20630
- const rows = stmt.all ? combined : deduplicateRows(combined, leftCols);
20631
- const warnings = [.../* @__PURE__ */ new Set([
20632
- ...leftResult.warnings ?? [],
20633
- ...rightResult.warnings ?? []
20634
- ])];
20635
- const result = {
20636
- type: "SELECT",
20637
- rows,
20638
- columns: leftCols,
20639
- rowCount: rows.length,
20640
- warnings
20641
- };
20642
- if (captureColumnMeta) {
20643
- materializedMetaBySelectResult.set(result, mergeUnionColumnMeta(leftResult, rightResult));
20644
- }
20645
- return result;
20974
+ return combineUnionResults(leftResult, rightResult, stmt.all, captureColumnMeta);
20646
20975
  });
20647
20976
  }
20648
20977
  function deduplicateRows(rows, columns) {
@@ -20654,6 +20983,442 @@ function deduplicateRows(rows, columns) {
20654
20983
  return true;
20655
20984
  });
20656
20985
  }
20986
+ function recursiveSelectOutputName(column) {
20987
+ if ("alias" in column && typeof column.alias === "string") return column.alias;
20988
+ if (column.type === "FIELD") return column.field;
20989
+ return null;
20990
+ }
20991
+ function recursiveOutputColumns(cte) {
20992
+ const spec = cte.recursiveSpec;
20993
+ if (cte.columnAliases) return [...cte.columnAliases];
20994
+ const names = spec.seed.columns.map(recursiveSelectOutputName);
20995
+ if (names.some((name) => name === null)) {
20996
+ throw new Error(
20997
+ `PlanningError: \u518D\u5E30 CTE\u300C${cte.name}\u300D\u3067\u5217\u540D\u30EA\u30B9\u30C8\u3092\u7701\u7565\u3059\u308B\u5834\u5408\u3001seed \u306E\u5F0F\u306B\u306F AS \u5225\u540D\u304C\u5FC5\u8981\u3067\u3059`
20998
+ );
20999
+ }
21000
+ const columns = names;
21001
+ if (new Set(columns).size !== columns.length) {
21002
+ throw new Error(
21003
+ `PlanningError: \u518D\u5E30 CTE\u300C${cte.name}\u300D\u3067\u5217\u540D\u30EA\u30B9\u30C8\u3092\u7701\u7565\u3059\u308B\u5834\u5408\u3001seed \u306E\u51FA\u529B\u5217\u540D\u3092\u91CD\u8907\u3055\u305B\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093`
21004
+ );
21005
+ }
21006
+ return columns;
21007
+ }
21008
+ function recursivePlanningColumnNames(stmt) {
21009
+ return stmt.columns.map((column, index) => recursiveSelectOutputName(column) ?? `__b53_column_${index}`);
21010
+ }
21011
+ function recursiveMetaCompatible(left, right) {
21012
+ const a = left.semantics;
21013
+ const b = right.semantics;
21014
+ if (!a || !b || a.compareMode === "unsupported" || b.compareMode === "unsupported") return false;
21015
+ if (a.compareMode !== b.compareMode) return false;
21016
+ if (left.sortKind && right.sortKind && left.sortKind !== right.sortKind) return false;
21017
+ if (a.inSubtable !== b.inSubtable || a.requiresCollectionOperators !== b.requiresCollectionOperators) return false;
21018
+ const synthetic = (fieldType) => fieldType === void 0 || fieldType.startsWith("KSQL_");
21019
+ if (!synthetic(left.fieldType) && !synthetic(right.fieldType) && left.fieldType !== right.fieldType) return false;
21020
+ return true;
21021
+ }
21022
+ async function buildRecursiveFieldResolver(stmt, client, cacheContext, materializedTables) {
21023
+ const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
21024
+ const physical = /* @__PURE__ */ new Map();
21025
+ await Promise.all(tables.filter((table) => table.cteName === null).map(async (table) => {
21026
+ if (physical.has(table.appId)) return;
21027
+ const infos = await getFieldsCached(table.appId, client, cacheContext);
21028
+ physical.set(table.appId, new Map(infos.map((info) => [info.code, info])));
21029
+ }));
21030
+ const resolveInTable = (table, field) => {
21031
+ if (table.cteName !== null) return materializedTables.get(table.cteName)?.columnMeta?.get(field);
21032
+ const info = physical.get(table.appId)?.get(fieldCodeForTypeLookup(table, field));
21033
+ return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(field);
21034
+ };
21035
+ return (ref) => {
21036
+ if (ref.tableAlias !== null) {
21037
+ const table = tables.find((candidate) => effectiveTableAlias(candidate) === ref.tableAlias);
21038
+ return table ? resolveInTable(table, ref.field) : void 0;
21039
+ }
21040
+ if (tables.length === 1) return resolveInTable(tables[0], ref.field);
21041
+ const matches = tables.map((table) => resolveInTable(table, ref.field)).filter(
21042
+ (meta) => meta !== void 0
21043
+ );
21044
+ return matches.length === 1 ? matches[0] : void 0;
21045
+ };
21046
+ }
21047
+ function validateRecursiveProjectionNode(value, resolveField2, numeric = false) {
21048
+ if (value === null || typeof value !== "object") return;
21049
+ if (Array.isArray(value)) {
21050
+ value.forEach((item) => validateRecursiveProjectionNode(item, resolveField2, numeric));
21051
+ return;
21052
+ }
21053
+ const node = value;
21054
+ if (node.type === "FIELD") {
21055
+ const ref = typeof node.tableAlias === "string" || node.tableAlias === null ? node : aggregateFieldRef(String(node.field ?? ""));
21056
+ const meta = resolveField2(ref);
21057
+ if (!meta?.semantics || meta.semantics.compareMode === "unsupported") {
21058
+ throw new Error(`PlanningError: \u518D\u5E30 CTE \u306E\u5C04\u5F71\u5217 ${ref.tableAlias ? `${ref.tableAlias}.` : ""}${ref.field} \u306E\u578B\u3092\u8A3C\u660E\u3067\u304D\u307E\u305B\u3093`);
21059
+ }
21060
+ if (numeric && meta.semantics.compareMode !== "number" && meta.semantics.compareMode !== "recordNumber") {
21061
+ throw new Error(`PlanningError: \u518D\u5E30 CTE \u306E\u6570\u5024\u6F14\u7B97\u306B\u6570\u5024\u3067\u306A\u3044\u5217 ${ref.field} \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
21062
+ }
21063
+ return;
21064
+ }
21065
+ if (node.type === "FIELD_REF" && typeof node.field === "string") {
21066
+ const ref = aggregateFieldRef(node.field);
21067
+ const meta = resolveField2(ref);
21068
+ if (!meta?.semantics || meta.semantics.compareMode === "unsupported") {
21069
+ throw new Error(`PlanningError: \u518D\u5E30 CTE \u306E\u5C04\u5F71\u5217 ${node.field} \u306E\u578B\u3092\u8A3C\u660E\u3067\u304D\u307E\u305B\u3093`);
21070
+ }
21071
+ if (numeric && meta.semantics.compareMode !== "number" && meta.semantics.compareMode !== "recordNumber") {
21072
+ throw new Error(`PlanningError: \u518D\u5E30 CTE \u306E\u6570\u5024\u6F14\u7B97\u306B\u6570\u5024\u3067\u306A\u3044\u5217 ${node.field} \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`);
21073
+ }
21074
+ return;
21075
+ }
21076
+ if (node.type === "ARITH" || node.type === "SCALAR_ARITH") {
21077
+ validateRecursiveProjectionNode(node.left, resolveField2, true);
21078
+ validateRecursiveProjectionNode(node.right, resolveField2, true);
21079
+ return;
21080
+ }
21081
+ for (const child of Object.values(node)) validateRecursiveProjectionNode(child, resolveField2, numeric);
21082
+ }
21083
+ async function inferRecursiveProjectionMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
21084
+ const resolveField2 = await buildRecursiveFieldResolver(stmt, client, cacheContext, materializedTables);
21085
+ stmt.columns.forEach((column) => validateRecursiveProjectionNode(column, resolveField2));
21086
+ const inferred = await inferSelectColumnMeta(
21087
+ stmt,
21088
+ recursivePlanningColumnNames(stmt),
21089
+ client,
21090
+ cacheContext,
21091
+ materializedTables
21092
+ );
21093
+ const sourceNames = recursivePlanningColumnNames(stmt);
21094
+ const aligned = /* @__PURE__ */ new Map();
21095
+ outputColumns.forEach((column, index) => {
21096
+ const meta = inferred.get(sourceNames[index]);
21097
+ if (!meta?.semantics || meta.semantics.fieldType === "KSQL_UNKNOWN" || meta.semantics.compareMode === "unsupported") {
21098
+ throw new Error(`PlanningError: \u518D\u5E30 CTE \u306E\u7B2C ${index + 1} \u5217\u306E\u578B\u3092\u9759\u7684\u306B\u8A3C\u660E\u3067\u304D\u307E\u305B\u3093`);
21099
+ }
21100
+ aligned.set(column, { ...meta, displayName: column });
21101
+ });
21102
+ return aligned;
21103
+ }
21104
+ function recursivePhysicalSourceKey(table) {
21105
+ return `${table.appId}:${table.subtableCode ?? ""}`;
21106
+ }
21107
+ function recursivePhysicalTables(...queries) {
21108
+ const byKey = /* @__PURE__ */ new Map();
21109
+ for (const query of queries) {
21110
+ for (const table of [query.from, ...query.joins.map((join2) => join2.table)]) {
21111
+ if (table.cteName === null && !byKey.has(recursivePhysicalSourceKey(table))) {
21112
+ byKey.set(recursivePhysicalSourceKey(table), table);
21113
+ }
21114
+ }
21115
+ }
21116
+ return [...byKey.values()];
21117
+ }
21118
+ function splitRecursiveFieldRef(field) {
21119
+ const dot = field.indexOf(".");
21120
+ return dot < 0 ? { alias: null, field } : { alias: field.slice(0, dot), field: field.slice(dot + 1) };
21121
+ }
21122
+ function collectRecursivePhysicalSourceFields(queries) {
21123
+ const result = /* @__PURE__ */ new Map();
21124
+ const markAll = (tables) => {
21125
+ for (const table of tables) result.set(recursivePhysicalSourceKey(table), null);
21126
+ };
21127
+ for (const query of queries) {
21128
+ const physical = physicalSelectTables(query);
21129
+ for (const table of physical) {
21130
+ const key = recursivePhysicalSourceKey(table);
21131
+ if (!result.has(key)) result.set(key, /* @__PURE__ */ new Set());
21132
+ }
21133
+ const addRef = (rawAlias, field) => {
21134
+ const table = rawAlias === null ? physical.length === 1 ? physical[0] : void 0 : [query.from, ...query.joins.map((join2) => join2.table)].find((candidate) => effectiveTableAlias(candidate)?.toLowerCase() === rawAlias.toLowerCase());
21135
+ if (!table) {
21136
+ markAll(physical);
21137
+ return;
21138
+ }
21139
+ if (table.cteName !== null) return;
21140
+ const current = result.get(recursivePhysicalSourceKey(table));
21141
+ if (current !== null) current?.add(field);
21142
+ };
21143
+ const visit = (node) => {
21144
+ if (node === null || node === void 0 || typeof node !== "object") return;
21145
+ if (Array.isArray(node)) {
21146
+ node.forEach(visit);
21147
+ return;
21148
+ }
21149
+ const value = node;
21150
+ if (value.type === "WILDCARD" || value.type === "PARENT_WILDCARD") {
21151
+ markAll(physical);
21152
+ return;
21153
+ }
21154
+ if ((value.type === "FIELD" || value.type === "FIELD_REF") && typeof value.field === "string") {
21155
+ const encoded = splitRecursiveFieldRef(value.field);
21156
+ const alias = typeof value.tableAlias === "string" ? value.tableAlias : value.tableAlias === null ? null : encoded.alias;
21157
+ addRef(alias, encoded.field);
21158
+ return;
21159
+ }
21160
+ if ((value.type === "FIELD_NAME" || value.type === "GROUPING_REF") && typeof value.name === "string") {
21161
+ markAll(physical);
21162
+ return;
21163
+ }
21164
+ Object.values(value).forEach(visit);
21165
+ };
21166
+ visit(query.columns);
21167
+ visit(query.where);
21168
+ visit(normalizeGroupingSpec(query));
21169
+ visit(query.having);
21170
+ visit(query.orderBy);
21171
+ for (const join2 of query.joins) {
21172
+ if (join2.on === null) {
21173
+ markAll(physical);
21174
+ continue;
21175
+ }
21176
+ addRef(join2.on.left.tableAlias, join2.on.left.field);
21177
+ addRef(join2.on.right.tableAlias, join2.on.right.field);
21178
+ }
21179
+ }
21180
+ return result;
21181
+ }
21182
+ function recursiveSourceSelect(table, fields) {
21183
+ const columns = fields !== null && fields.size > 0 ? [...fields].map((field) => ({ type: "FIELD", field, alias: null })) : [{ type: "WILDCARD" }];
21184
+ return {
21185
+ type: "SELECT",
21186
+ distinct: false,
21187
+ columns,
21188
+ from: { ...table, alias: null },
21189
+ joins: [],
21190
+ where: null,
21191
+ groupBy: [],
21192
+ having: null,
21193
+ orderMode: "CANONICAL",
21194
+ orderBy: [],
21195
+ limit: null,
21196
+ offset: null
21197
+ };
21198
+ }
21199
+ async function materializeRecursivePhysicalSources(queries, client, options, cacheContext) {
21200
+ const fieldsBySource = collectRecursivePhysicalSourceFields(queries);
21201
+ const entries = await Promise.all(recursivePhysicalTables(...queries).map(async (table, index) => {
21202
+ const result = await executeSelect(
21203
+ recursiveSourceSelect(table, fieldsBySource.get(recursivePhysicalSourceKey(table)) ?? null),
21204
+ client,
21205
+ { ...options, onLimitReached: "error" },
21206
+ cacheContext,
21207
+ void 0,
21208
+ true,
21209
+ false,
21210
+ "DERIVED"
21211
+ );
21212
+ const name = `__b53_source_${index}`;
21213
+ return [recursivePhysicalSourceKey(table), {
21214
+ name,
21215
+ warnings: result.warnings ?? [],
21216
+ table: {
21217
+ rows: result.rows,
21218
+ columns: result.columns,
21219
+ columnMeta: materializedMetaBySelectResult.get(result)
21220
+ }
21221
+ }];
21222
+ }));
21223
+ return new Map(entries);
21224
+ }
21225
+ function rewriteRecursivePhysicalSources(stmt, sources) {
21226
+ const rewrite = (table) => {
21227
+ if (table.cteName !== null) return table;
21228
+ const source = sources.get(recursivePhysicalSourceKey(table));
21229
+ if (!source) throw new Error("PlanningError: \u518D\u5E30 CTE \u306E\u5B8C\u5168\u5B9F\u4F53\u5316 source \u3092\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093");
21230
+ return { appId: 0, alias: table.alias, cteName: source.name, subtableCode: null };
21231
+ };
21232
+ return {
21233
+ ...stmt,
21234
+ from: rewrite(stmt.from),
21235
+ joins: stmt.joins.map((join2) => ({ ...join2, table: rewrite(join2.table) }))
21236
+ };
21237
+ }
21238
+ function tableMetaForJoinKey(table, field, cache) {
21239
+ return table.cteName === null ? void 0 : cache.get(table.cteName)?.columnMeta?.get(field);
21240
+ }
21241
+ function recursiveJoinKey(value, semantics) {
21242
+ if (semantics.compareMode !== "number" && semantics.compareMode !== "recordNumber") return value;
21243
+ const decimal = parseExactDecimal(value);
21244
+ return decimal === null ? `invalid:${value}` : `${decimal.sign}:${decimal.coefficient}:${decimal.scale}`;
21245
+ }
21246
+ function recursiveJoinSides(cteName, term) {
21247
+ const join2 = term.joins[0];
21248
+ if (!join2 || join2.type !== "INNER") throw new Error("PlanningError: \u518D\u5E30\u9805\u306E INNER JOIN \u3092\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093");
21249
+ const self = term.from.cteName === cteName ? term.from : join2.table;
21250
+ const source = self === term.from ? join2.table : term.from;
21251
+ const selfAlias = effectiveTableAlias(self);
21252
+ const leftIsSelf = join2.on.left.tableAlias === selfAlias;
21253
+ const rightIsSelf = join2.on.right.tableAlias === selfAlias;
21254
+ if (leftIsSelf === rightIsSelf) throw new Error("PlanningError: \u518D\u5E30\u9805\u306E\u81EA\u5DF1\u53C2\u7167 JOIN \u30AD\u30FC\u3092\u4E00\u610F\u306B\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093");
21255
+ return {
21256
+ self,
21257
+ source,
21258
+ selfField: leftIsSelf ? join2.on.left.field : join2.on.right.field,
21259
+ sourceField: leftIsSelf ? join2.on.right.field : join2.on.left.field
21260
+ };
21261
+ }
21262
+ async function executeRecursiveCte(cte, client, options, cteCache, cacheContext) {
21263
+ const spec = cte.recursiveSpec;
21264
+ const outputColumns = recursiveOutputColumns(cte);
21265
+ const seedMeta = await inferRecursiveProjectionMeta(
21266
+ spec.seed,
21267
+ outputColumns,
21268
+ client,
21269
+ cacheContext,
21270
+ cteCache
21271
+ );
21272
+ const planningCache = new Map(cteCache);
21273
+ planningCache.set(cte.name, { rows: [], columns: outputColumns, columnMeta: seedMeta });
21274
+ const termMeta = await inferRecursiveProjectionMeta(
21275
+ spec.recursiveTerm,
21276
+ outputColumns,
21277
+ client,
21278
+ cacheContext,
21279
+ planningCache
21280
+ );
21281
+ outputColumns.forEach((column, index) => {
21282
+ const left = seedMeta.get(column);
21283
+ const right = termMeta.get(column);
21284
+ if (!left || !right || !recursiveMetaCompatible(left, right)) {
21285
+ throw new Error(`PlanningError: \u518D\u5E30 CTE\u300C${cte.name}\u300D\u306E\u7B2C ${index + 1} \u5217\u3067 seed \u3068\u518D\u5E30\u9805\u306E\u578B\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`);
21286
+ }
21287
+ });
21288
+ const planningSides = recursiveJoinSides(cte.name, spec.recursiveTerm);
21289
+ const termFieldResolver = await buildRecursiveFieldResolver(
21290
+ spec.recursiveTerm,
21291
+ client,
21292
+ cacheContext,
21293
+ planningCache
21294
+ );
21295
+ const selfJoinMeta = termFieldResolver({
21296
+ type: "FIELD",
21297
+ tableAlias: effectiveTableAlias(planningSides.self),
21298
+ field: planningSides.selfField
21299
+ });
21300
+ const sourceJoinMeta = termFieldResolver({
21301
+ type: "FIELD",
21302
+ tableAlias: effectiveTableAlias(planningSides.source),
21303
+ field: planningSides.sourceField
21304
+ });
21305
+ if (!selfJoinMeta || !sourceJoinMeta || !recursiveMetaCompatible(selfJoinMeta, sourceJoinMeta)) {
21306
+ throw new Error(`PlanningError: \u518D\u5E30 CTE\u300C${cte.name}\u300D\u306E\u81EA\u5DF1\u53C2\u7167 JOIN \u30AD\u30FC\u306E\u578B\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093`);
21307
+ }
21308
+ const sources = await materializeRecursivePhysicalSources(
21309
+ [spec.seed, spec.recursiveTerm],
21310
+ client,
21311
+ options,
21312
+ cacheContext
21313
+ );
21314
+ const runtimeCache = new Map(cteCache);
21315
+ for (const source of sources.values()) runtimeCache.set(source.name, source.table);
21316
+ const seedQuery = rewriteRecursivePhysicalSources(spec.seed, sources);
21317
+ const termQuery = rewriteRecursivePhysicalSources(spec.recursiveTerm, sources);
21318
+ const rawSeed = await executeQueryWithCte(seedQuery, client, options, runtimeCache, cacheContext, true, true);
21319
+ const seed = alignSelectResultByPosition(rawSeed, outputColumns, seedMeta);
21320
+ const cycle = spec.cycle;
21321
+ const cycleMeta = cycle ? seedMeta.get(cycle.column) : void 0;
21322
+ const resultMeta = new Map(seedMeta);
21323
+ if (cycle) resultMeta.set(cycle.markColumn, { ...syntheticColumnMeta("string"), displayName: cycle.markColumn });
21324
+ const resultColumns = cycle ? [...outputColumns, cycle.markColumn] : [...outputColumns];
21325
+ const rows = [];
21326
+ let frontier = [];
21327
+ const warnings = /* @__PURE__ */ new Set([
21328
+ ...seed.warnings ?? [],
21329
+ ...[...sources.values()].flatMap((source) => source.warnings)
21330
+ ]);
21331
+ const limits = new RecursiveCteLimitCounter(cte.name, resolveRecursiveCteLimits(options));
21332
+ const append = (row) => {
21333
+ limits.addRow();
21334
+ rows.push(row);
21335
+ };
21336
+ for (const row of seed.rows) {
21337
+ const value = cycle ? String(row[cycle.column] ?? "") : "";
21338
+ const emitted = cycle ? { ...row, [cycle.markColumn]: cycle.defaultValue } : row;
21339
+ append(emitted);
21340
+ frontier.push({ row, path: cycle ? [value] : [] });
21341
+ }
21342
+ const sides = recursiveJoinSides(cte.name, termQuery);
21343
+ const sourceTable = sides.source.cteName === null ? void 0 : runtimeCache.get(sides.source.cteName);
21344
+ if (!sourceTable) throw new Error("PlanningError: \u518D\u5E30\u9805\u306E\u5B8C\u5168\u5B9F\u4F53\u5316 source \u304C\u3042\u308A\u307E\u305B\u3093");
21345
+ const joinMeta = tableMetaForJoinKey(sides.self, sides.selfField, planningCache) ?? tableMetaForJoinKey(sides.source, sides.sourceField, runtimeCache);
21346
+ if (!joinMeta?.semantics || joinMeta.semantics.compareMode === "unsupported") {
21347
+ throw new Error(`PlanningError: \u518D\u5E30\u9805\u306E JOIN \u30AD\u30FC ${sides.selfField} \u306E\u578B\u3092\u8A3C\u660E\u3067\u304D\u307E\u305B\u3093`);
21348
+ }
21349
+ const sourceRowsByKey = /* @__PURE__ */ new Map();
21350
+ for (const sourceRow of sourceTable.rows) {
21351
+ const value = String(sourceRow[sides.sourceField] ?? "");
21352
+ const key = recursiveJoinKey(value, joinMeta.semantics);
21353
+ const bucket = sourceRowsByKey.get(key);
21354
+ if (bucket) bucket.push(sourceRow);
21355
+ else sourceRowsByKey.set(key, [sourceRow]);
21356
+ }
21357
+ const sourceHasEmptyKey = sourceRowsByKey.has(recursiveJoinKey("", joinMeta.semantics));
21358
+ let depth = 0;
21359
+ let emptyKeyWarned = false;
21360
+ while (frontier.length > 0) {
21361
+ depth++;
21362
+ const next = [];
21363
+ for (const parent of frontier) {
21364
+ const parentKey = String(parent.row[sides.selfField] ?? "");
21365
+ if (!emptyKeyWarned && parentKey === "" && sourceHasEmptyKey) {
21366
+ warnings.add(
21367
+ `\u518D\u5E30 CTE\u300C${cte.name}\u300D\u306E JOIN ${sides.selfField} = ${sides.sourceField} \u3067\u7B2C ${depth} \u53CD\u5FA9\u306B\u4E21\u5074\u306E\u7A7A\u30AD\u30FC\u3092\u691C\u51FA\u3057\u307E\u3057\u305F\u3002\u7A7A\u30AD\u30FC\u3069\u3046\u3057\u306F\u4E00\u81F4\u3057\u3001\u30EB\u30FC\u30C8\u7FA4\u3092\u518D\u5C55\u958B\u3057\u5F97\u307E\u3059\u3002`
21368
+ );
21369
+ emptyKeyWarned = true;
21370
+ }
21371
+ const matchingSourceRows = sourceRowsByKey.get(recursiveJoinKey(parentKey, joinMeta.semantics)) ?? [];
21372
+ for (const sourceRow of matchingSourceRows) {
21373
+ const sourceKey = String(sourceRow[sides.sourceField] ?? "");
21374
+ if (!compareScalarValues("=", parentKey, sourceKey, joinMeta.semantics)) continue;
21375
+ limits.addExpansion();
21376
+ }
21377
+ if (matchingSourceRows.length === 0) continue;
21378
+ const iterationCache = new Map(runtimeCache);
21379
+ iterationCache.set(cte.name, {
21380
+ rows: [parent.row],
21381
+ columns: outputColumns,
21382
+ columnMeta: seedMeta
21383
+ });
21384
+ const rawCandidates = await executeQueryWithCte(
21385
+ termQuery,
21386
+ client,
21387
+ options,
21388
+ iterationCache,
21389
+ cacheContext,
21390
+ true,
21391
+ true
21392
+ );
21393
+ for (const warning of rawCandidates.warnings ?? []) warnings.add(warning);
21394
+ const candidates = alignSelectResultByPosition(rawCandidates, outputColumns, seedMeta);
21395
+ if (candidates.rows.length > 0) limits.observeDepth(depth);
21396
+ for (const candidate of candidates.rows) {
21397
+ if (!cycle) {
21398
+ append(candidate);
21399
+ next.push({ row: candidate, path: [] });
21400
+ continue;
21401
+ }
21402
+ const value = String(candidate[cycle.column] ?? "");
21403
+ const isCycle = parent.path.some(
21404
+ (seen) => compareScalarValues("=", seen, value, cycleMeta.semantics)
21405
+ );
21406
+ append({ ...candidate, [cycle.markColumn]: isCycle ? cycle.markValue : cycle.defaultValue });
21407
+ if (!isCycle) next.push({ row: candidate, path: [...parent.path, value] });
21408
+ }
21409
+ }
21410
+ frontier = next;
21411
+ }
21412
+ const result = {
21413
+ type: "SELECT",
21414
+ rows,
21415
+ columns: resultColumns,
21416
+ rowCount: rows.length,
21417
+ warnings: [...warnings]
21418
+ };
21419
+ materializedMetaBySelectResult.set(result, resultMeta);
21420
+ return result;
21421
+ }
20657
21422
  async function executeWith(stmt, client, options, cacheContext, seed, captureColumnMeta = false) {
20658
21423
  if ((seed == null || seed.size === 0) && canInlineSingleCte(stmt)) {
20659
21424
  return executeSelect(
@@ -20671,7 +21436,9 @@ async function executeWith(stmt, client, options, cacheContext, seed, captureCol
20671
21436
  const warnings = /* @__PURE__ */ new Set();
20672
21437
  for (const cte of stmt.ctes) {
20673
21438
  let result2;
20674
- if (cte.query.type === "SHOW_APPS") {
21439
+ if (cte.recursiveSpec) {
21440
+ result2 = await executeRecursiveCte(cte, client, options, cteCache, cacheContext);
21441
+ } else if (cte.query.type === "SHOW_APPS") {
20675
21442
  result2 = await executeShowApps(client);
20676
21443
  } else if (cte.query.type === "DESCRIBE") {
20677
21444
  result2 = await executeDescribe(cte.query, client, cacheContext);
@@ -20728,33 +21495,7 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
20728
21495
  executeQueryWithCte(query.left, client, options, cteCache, cacheContext, captureColumnMeta, true),
20729
21496
  executeQueryWithCte(query.right, client, options, cteCache, cacheContext, captureColumnMeta, true)
20730
21497
  ]);
20731
- const leftCols = leftResult.columns;
20732
- const rightCols = rightResult.columns;
20733
- assertUnionColumnCount(leftCols, rightCols);
20734
- const remapped = rightResult.rows.map((row) => {
20735
- const mapped = {};
20736
- leftCols.forEach((col, i) => {
20737
- mapped[col] = row[rightCols[i] ?? col] ?? "";
20738
- });
20739
- return mapped;
20740
- });
20741
- const combined = [...leftResult.rows, ...remapped];
20742
- const rows = query.all ? combined : deduplicateRows(combined, leftCols);
20743
- const warnings = [.../* @__PURE__ */ new Set([
20744
- ...leftResult.warnings ?? [],
20745
- ...rightResult.warnings ?? []
20746
- ])];
20747
- const result2 = {
20748
- type: "SELECT",
20749
- rows,
20750
- columns: leftCols,
20751
- rowCount: rows.length,
20752
- warnings
20753
- };
20754
- if (captureColumnMeta) {
20755
- materializedMetaBySelectResult.set(result2, mergeUnionColumnMeta(leftResult, rightResult));
20756
- }
20757
- return result2;
21498
+ return combineUnionResults(leftResult, rightResult, query.all, captureColumnMeta);
20758
21499
  }
20759
21500
  const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
20760
21501
  if (!hasCteRef) {
@@ -20832,9 +21573,9 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
20832
21573
  const warnings = /* @__PURE__ */ new Set();
20833
21574
  const parallel = options.fetchParallel ?? 1;
20834
21575
  await Promise.all([
20835
- resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
20836
- resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
20837
- resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
21576
+ resolveSubqueries(stmt.where, client, options, cacheContext, warnings, cteCache),
21577
+ resolveSubqueries(stmt.having, client, options, cacheContext, warnings, cteCache),
21578
+ resolveSelectCaseSubqueries(stmt, client, options, cacheContext, warnings, cteCache)
20838
21579
  ]);
20839
21580
  const whereCapability = classifyWhereCapability(stmt.where, choiceAndWindowResolver);
20840
21581
  if (whereCapability.capability === "UNSUPPORTED") {
@@ -20874,6 +21615,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
20874
21615
  client,
20875
21616
  effectiveOptions,
20876
21617
  cacheContext,
21618
+ warnings,
20877
21619
  cteCache
20878
21620
  );
20879
21621
  const orderByMetaPromise = Promise.resolve(orderMeta);
@@ -24337,17 +25079,17 @@ function parseSql(sql, enableImport = false) {
24337
25079
  throw e;
24338
25080
  }
24339
25081
  }
24340
- async function resolveSubqueries(where, client, options, cacheContext, cteCache) {
25082
+ async function resolveSubqueries(where, client, options, cacheContext, warnings, cteCache) {
24341
25083
  const tasks = [];
24342
- collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache);
25084
+ collectSubqueryTasks(where, client, options, cacheContext, tasks, warnings, cteCache);
24343
25085
  await Promise.all(tasks);
24344
25086
  }
24345
- async function resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache) {
25087
+ async function resolveSelectCaseSubqueries(stmt, client, options, cacheContext, warnings, cteCache) {
24346
25088
  const tasks = [];
24347
25089
  for (const column of stmt.columns) {
24348
25090
  if (column.type !== "CASE_COL") continue;
24349
25091
  for (const branch of column.expr.branches) {
24350
- tasks.push(resolveSubqueries(branch.condition, client, options, cacheContext, cteCache));
25092
+ tasks.push(resolveSubqueries(branch.condition, client, options, cacheContext, warnings, cteCache));
24351
25093
  }
24352
25094
  }
24353
25095
  await Promise.all(tasks);
@@ -24358,19 +25100,21 @@ function runSubquery(query, client, options, cacheContext, cteCache) {
24358
25100
  }
24359
25101
  return executeSelect(query, client, options, cacheContext);
24360
25102
  }
24361
- function collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCache) {
25103
+ function collectSubqueryTasks(where, client, options, cacheContext, tasks, warnings, cteCache) {
24362
25104
  if (where === null) return;
24363
25105
  switch (where.type) {
24364
25106
  case "BINARY": {
24365
25107
  const right = where.right;
24366
25108
  if (right.type === "SUBQUERY_IN_LIST") {
24367
25109
  tasks.push(runSubquery(right.query, client, options, cacheContext, cteCache).then((result) => {
25110
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24368
25111
  const col = right.column ?? (result.columns[0] ?? "");
24369
25112
  right.resolved = new Set(result.rows.map((r) => r[col] ?? ""));
24370
25113
  }));
24371
25114
  }
24372
25115
  if (right.type === "SCALAR_SUBQUERY") {
24373
25116
  tasks.push(runSubquery(right.query, client, options, cacheContext, cteCache).then((result) => {
25117
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24374
25118
  if (result.rowCount === 0) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u5024\u3092\u8FD4\u3057\u307E\u305B\u3093\u3067\u3057\u305F");
24375
25119
  if (result.rowCount > 1) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u8907\u6570\u884C\u3092\u8FD4\u3057\u307E\u3057\u305F\uFF081\u884C\u306E\u307F\u8A31\u53EF\uFF09");
24376
25120
  const col = result.columns[0] ?? "";
@@ -24380,16 +25124,17 @@ function collectSubqueryTasks(where, client, options, cacheContext, tasks, cteCa
24380
25124
  break;
24381
25125
  }
24382
25126
  case "LOGICAL":
24383
- collectSubqueryTasks(where.left, client, options, cacheContext, tasks, cteCache);
24384
- collectSubqueryTasks(where.right, client, options, cacheContext, tasks, cteCache);
25127
+ collectSubqueryTasks(where.left, client, options, cacheContext, tasks, warnings, cteCache);
25128
+ collectSubqueryTasks(where.right, client, options, cacheContext, tasks, warnings, cteCache);
24385
25129
  break;
24386
25130
  case "NOT":
24387
25131
  case "GROUP":
24388
- collectSubqueryTasks(where.expr, client, options, cacheContext, tasks, cteCache);
25132
+ collectSubqueryTasks(where.expr, client, options, cacheContext, tasks, warnings, cteCache);
24389
25133
  break;
24390
25134
  case "EXISTS": {
24391
25135
  const node = where;
24392
25136
  tasks.push(runSubquery(node.query, client, options, cacheContext, cteCache).then((result) => {
25137
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24393
25138
  node.resolved = result.rowCount > 0;
24394
25139
  }));
24395
25140
  break;
@@ -24409,7 +25154,7 @@ async function resolveSetSubqueries(assignments, client, options, cacheContext)
24409
25154
  a.value = { type: "STRING", value: resolved };
24410
25155
  }
24411
25156
  }
24412
- async function resolveScalarColumns(columns, client, options, cacheContext, cteCache) {
25157
+ async function resolveScalarColumns(columns, client, options, cacheContext, warnings, cteCache) {
24413
25158
  const byQuery = /* @__PURE__ */ new Map();
24414
25159
  const pending = [];
24415
25160
  for (let i = 0; i < columns.length; i++) {
@@ -24419,6 +25164,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
24419
25164
  let promise = byQuery.get(key);
24420
25165
  if (!promise) {
24421
25166
  promise = runSubquery(col.query, client, options, cacheContext, cteCache).then((result) => {
25167
+ for (const warning of result.warnings ?? []) warnings.add(warning);
24422
25168
  if (result.rowCount === 0) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u5024\u3092\u8FD4\u3057\u307E\u305B\u3093\u3067\u3057\u305F");
24423
25169
  if (result.rowCount > 1) throw new Error("\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA\u304C\u8907\u6570\u884C\u3092\u8FD4\u3057\u307E\u3057\u305F\uFF081\u884C\u306E\u307F\u8A31\u53EF\uFF09");
24424
25170
  const firstCol = result.columns[0] ?? "";
@@ -25286,7 +26032,12 @@ var EXPLAIN_FETCH_PLAN = /* @__PURE__ */ Symbol("ksql.explainFetchPlan");
25286
26032
  function setExplainFetchPlan(result, plan) {
25287
26033
  result[EXPLAIN_FETCH_PLAN] = plan;
25288
26034
  }
25289
- async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, resolveMetadata = true) {
26035
+ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, resolveMetadata = true, recursiveCteMaxDepth, recursiveCteMaxRows, recursiveCteMaxExpansions) {
26036
+ const recursiveLimits = resolveRecursiveCteLimits({
26037
+ recursiveCteMaxDepth,
26038
+ recursiveCteMaxRows,
26039
+ recursiveCteMaxExpansions
26040
+ });
25290
26041
  const invocationCacheContext = createInvocationCacheContext(cacheContext);
25291
26042
  try {
25292
26043
  const statements = parseSqlBatch(sql, enableImport);
@@ -25365,7 +26116,8 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
25365
26116
  dmlMaxSubtableRows,
25366
26117
  fetchCollector,
25367
26118
  tempSchemaLedger,
25368
- createdSchema
26119
+ createdSchema,
26120
+ { maxRecords, recursiveLimits }
25369
26121
  ), cursorMaxActive)
25370
26122
  ];
25371
26123
  const metadataPlan = explainMetadataLines(whereAnalysis);
@@ -25397,7 +26149,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
25397
26149
  releaseMetadataCacheScope(invocationCacheContext);
25398
26150
  }
25399
26151
  }
25400
- function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGroupByPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }, tempSchemaLedger = /* @__PURE__ */ new Map(), createdSchema) {
26152
+ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGroupByPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }, tempSchemaLedger = /* @__PURE__ */ new Map(), createdSchema, explainContext = defaultRecursiveExplainContext()) {
25401
26153
  if (stmt.type === "CREATE_TEMP_TABLE") {
25402
26154
  return [
25403
26155
  `CREATE TEMP TABLE ${stmt.name}`,
@@ -25418,7 +26170,8 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
25418
26170
  plainGroupByPlans,
25419
26171
  collector,
25420
26172
  "main",
25421
- tempSchemaLedger
26173
+ tempSchemaLedger,
26174
+ explainContext
25422
26175
  ).map((l) => ` ${l}`)
25423
26176
  ];
25424
26177
  }
@@ -25443,7 +26196,8 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
25443
26196
  plainGroupByPlans,
25444
26197
  collector,
25445
26198
  "main",
25446
- tempSchemaLedger
26199
+ tempSchemaLedger,
26200
+ explainContext
25447
26201
  ).map((l) => ` ${l}`)
25448
26202
  ];
25449
26203
  }
@@ -25475,7 +26229,8 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
25475
26229
  plainGroupByPlans,
25476
26230
  collector,
25477
26231
  "main",
25478
- tempSchemaLedger
26232
+ tempSchemaLedger,
26233
+ explainContext
25479
26234
  );
25480
26235
  }
25481
26236
  if (stmt.type === "ASSERT") {
@@ -25497,7 +26252,8 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
25497
26252
  plainGroupByPlans,
25498
26253
  collector,
25499
26254
  "main",
25500
- tempSchemaLedger
26255
+ tempSchemaLedger,
26256
+ explainContext
25501
26257
  ).map((l) => ` ${l}`));
25502
26258
  });
25503
26259
  return lines;
@@ -25522,7 +26278,8 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGrou
25522
26278
  plainGroupByPlans,
25523
26279
  collector,
25524
26280
  "main",
25525
- tempSchemaLedger
26281
+ tempSchemaLedger,
26282
+ explainContext
25526
26283
  );
25527
26284
  }
25528
26285
  function hasTempTableRef(node) {
@@ -25535,7 +26292,7 @@ function hasTempTableRef(node) {
25535
26292
  }
25536
26293
  return false;
25537
26294
  }
25538
- function buildPlanForBatchQuery(query, info, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }, sourceRole = "main", tempSchemaLedger = /* @__PURE__ */ new Map()) {
26295
+ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }, sourceRole = "main", tempSchemaLedger = /* @__PURE__ */ new Map(), explainContext = defaultRecursiveExplainContext()) {
25539
26296
  if (info.tempTablesReferenced.length === 0) {
25540
26297
  return buildExplainPlan(
25541
26298
  query,
@@ -25544,11 +26301,12 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, plainGrou
25544
26301
  orderPlans,
25545
26302
  100,
25546
26303
  DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
25547
- 1e4,
26304
+ explainContext.maxRecords,
25548
26305
  plainGroupByPlans,
25549
26306
  true,
25550
26307
  collector,
25551
- sourceRole
26308
+ sourceRole,
26309
+ explainContext.recursiveLimits
25552
26310
  );
25553
26311
  }
25554
26312
  const lines = [];
@@ -25595,7 +26353,15 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, plainGrou
25595
26353
  return lines;
25596
26354
  }
25597
26355
  var explainMaterializedTables = /* @__PURE__ */ new WeakMap();
25598
- async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxActive, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, relativeDatePlan) {
26356
+ function defaultRecursiveExplainContext() {
26357
+ return { maxRecords: 1e4, recursiveLimits: resolveRecursiveCteLimits({}) };
26358
+ }
26359
+ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxActive, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, relativeDatePlan, recursiveCteMaxDepth, recursiveCteMaxRows, recursiveCteMaxExpansions) {
26360
+ const recursiveLimits = resolveRecursiveCteLimits({
26361
+ recursiveCteMaxDepth,
26362
+ recursiveCteMaxRows,
26363
+ recursiveCteMaxExpansions
26364
+ });
25599
26365
  const sharedPlan = relativeDatePlan ?? await resolveRelativeDateExecutionPlan(stmt.query, client, cacheContext);
25600
26366
  const analysis = await buildExplainWhereAnalysis(
25601
26367
  stmt.query,
@@ -25621,7 +26387,9 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
25621
26387
  maxRecords,
25622
26388
  analysis.plainGroupByPlans,
25623
26389
  true,
25624
- fetchCollector
26390
+ fetchCollector,
26391
+ "main",
26392
+ recursiveLimits
25625
26393
  ),
25626
26394
  cursorMaxActive
25627
26395
  )
@@ -25653,7 +26421,7 @@ function addCursorConcurrency(lines, cursorMaxActive) {
25653
26421
  }
25654
26422
  return result;
25655
26423
  }
25656
- function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4, plainGroupByPlans, includeFetchSummary = true, collector, sourceRole = "main") {
26424
+ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4, plainGroupByPlans, includeFetchSummary = true, collector, sourceRole = "main", recursiveLimits = resolveRecursiveCteLimits({})) {
25657
26425
  const fetchCollector = collector ?? { sources: [] };
25658
26426
  if (query.type === "UNION") {
25659
26427
  const lines2 = buildUnionPlan(
@@ -25671,7 +26439,9 @@ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 1
25671
26439
  capabilities,
25672
26440
  orderPlans,
25673
26441
  plainGroupByPlans,
25674
- fetchCollector
26442
+ fetchCollector,
26443
+ maxRecords,
26444
+ recursiveLimits
25675
26445
  );
25676
26446
  return includeFetchSummary ? addFetchSummary(lines2, fetchCollector.sources) : lines2;
25677
26447
  }
@@ -26268,11 +27038,30 @@ function populateWithCrossJoinExplain(stmt) {
26268
27038
  }
26269
27039
  analyzeQuery(stmt.query);
26270
27040
  }
26271
- function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
27041
+ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }, maxRecords = 1e4, recursiveLimits = resolveRecursiveCteLimits({})) {
26272
27042
  populateWithCrossJoinExplain(stmt);
26273
27043
  const lines = [];
26274
27044
  for (const cte of stmt.ctes) {
26275
- if (cte.query.type === "SELECT") {
27045
+ if (cte.recursiveSpec) {
27046
+ const cycle = cte.recursiveSpec.cycle;
27047
+ lines.push(
27048
+ `recursive cte: ${cte.name}`,
27049
+ " strategy: B (materialize each source once, iterate in memory)",
27050
+ " union: UNION ALL",
27051
+ " self reference: once",
27052
+ cycle ? ` cycle: path-scoped on ${cycle.column}, mark ${cycle.markColumn} ('${cycle.markValue}'/'${cycle.defaultValue}'), cycle row emitted, expansion stopped` : " cycle: none (absolute limits still enforced)",
27053
+ ` limits: depth=${recursiveLimits.depth}, rows=${recursiveLimits.rows}, expansions=${recursiveLimits.expansions} (always fail-closed)`,
27054
+ " complete input: required (onLimit=truncate disabled)",
27055
+ " empty-key recursive join: runtime checked"
27056
+ );
27057
+ for (const source of recursivePhysicalTables(cte.recursiveSpec.seed, cte.recursiveSpec.recursiveTerm)) {
27058
+ const sourceName = `APP${source.appId}${source.subtableCode ? `$${source.subtableCode}` : ""}`;
27059
+ lines.push(
27060
+ ` source ${sourceName}: R unknown, pageSize=500, estimated calls=ceil(R/500), maxRecords=${maxRecords}`
27061
+ );
27062
+ }
27063
+ lines.push(" iteration rows: unknown until execution", " records API: none", "");
27064
+ } else if (cte.query.type === "SELECT") {
26276
27065
  lines.push(...buildSelectPlan(
26277
27066
  cte.query,
26278
27067
  `[cte: ${cte.name}]`,
@@ -26338,10 +27127,12 @@ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collec
26338
27127
  orderPlans,
26339
27128
  100,
26340
27129
  DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
26341
- 1e4,
27130
+ maxRecords,
26342
27131
  plainGroupByPlans,
26343
27132
  false,
26344
- collector
27133
+ collector,
27134
+ "main",
27135
+ recursiveLimits
26345
27136
  ));
26346
27137
  }
26347
27138
  if (canInlineSingleCte(stmt)) {
@@ -28693,6 +29484,16 @@ Options:
28693
29484
  -h, --help Show help
28694
29485
  -v, --version Show version
28695
29486
  `;
29487
+ var RECURSIVE_CTE_HELP_LINES = [
29488
+ " --recursive-cte-max-depth <n> Max recursive CTE depth (default: 100)",
29489
+ " --recursive-cte-max-rows <n> Max accumulated recursive CTE rows (default: 10000)",
29490
+ " --recursive-cte-max-expansions <n> Max recursive CTE candidate expansions (default: 100000)"
29491
+ ].join("\n");
29492
+ var CLI_HELP_TEXT = HELP_TEXT.replace(
29493
+ " --max-records <n> Max records to fetch (default: 500)",
29494
+ ` --max-records <n> Max records to fetch (default: 500)
29495
+ ${RECURSIVE_CTE_HELP_LINES}`
29496
+ );
28696
29497
  var CLI_IMPORT_SOURCE_REQUIRED_MESSAGE = "IMPORT \u306B\u306F\u30BD\u30FC\u30B9\u304C\u5FC5\u8981\u3067\u3059\u3002--import-csv <name=path> \u307E\u305F\u306F --import-json <name=path> \u3067\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
28697
29498
  function toCliImportError(error, importEnabled) {
28698
29499
  if (importEnabled || !isImportCapabilityGateError(error)) return error;
@@ -28712,6 +29513,9 @@ function parseArgs(argv) {
28712
29513
  dryRun: false,
28713
29514
  format: null,
28714
29515
  maxRecords: null,
29516
+ recursiveCteMaxDepth: null,
29517
+ recursiveCteMaxRows: null,
29518
+ recursiveCteMaxExpansions: null,
28715
29519
  fetchParallel: null,
28716
29520
  onLimit: null,
28717
29521
  tempTableMaxRows: null,
@@ -28978,6 +29782,17 @@ function parseArgs(argv) {
28978
29782
  i++;
28979
29783
  continue;
28980
29784
  }
29785
+ if (a === "--recursive-cte-max-depth" || a === "--recursive-cte-max-rows" || a === "--recursive-cte-max-expansions") {
29786
+ const n = Number(v);
29787
+ if (!Number.isSafeInteger(n) || n <= 0) {
29788
+ throw new Error(`ArgumentError: ${a} must be a positive safe integer.`);
29789
+ }
29790
+ if (a === "--recursive-cte-max-depth") out.recursiveCteMaxDepth = n;
29791
+ else if (a === "--recursive-cte-max-rows") out.recursiveCteMaxRows = n;
29792
+ else out.recursiveCteMaxExpansions = n;
29793
+ i++;
29794
+ continue;
29795
+ }
28981
29796
  if (a === "--temp-table-max-rows") {
28982
29797
  const n = Number(v);
28983
29798
  if (!Number.isInteger(n) || n <= 0) throw new Error("ArgumentError: --temp-table-max-rows must be a positive integer.");
@@ -29606,6 +30421,9 @@ function buildReplExecArgv(base, sql, dryRun, format) {
29606
30421
  pushOpt(argv, "--token-file", base.tokenFile);
29607
30422
  pushOpt(argv, "--app", base.app);
29608
30423
  pushOpt(argv, "--max-records", base.maxRecords);
30424
+ pushOpt(argv, "--recursive-cte-max-depth", base.recursiveCteMaxDepth);
30425
+ pushOpt(argv, "--recursive-cte-max-rows", base.recursiveCteMaxRows);
30426
+ pushOpt(argv, "--recursive-cte-max-expansions", base.recursiveCteMaxExpansions);
29609
30427
  pushOpt(argv, "--fetch-parallel", base.fetchParallel);
29610
30428
  pushOpt(argv, "--on-limit", base.onLimit);
29611
30429
  pushOpt(argv, "--temp-table-max-rows", base.tempTableMaxRows);
@@ -30086,7 +30904,7 @@ async function run() {
30086
30904
  return 2;
30087
30905
  }
30088
30906
  if (args.help) {
30089
- process.stdout.write(`${HELP_TEXT}
30907
+ process.stdout.write(`${CLI_HELP_TEXT}
30090
30908
  `);
30091
30909
  return 0;
30092
30910
  }
@@ -30204,6 +31022,20 @@ async function run() {
30204
31022
  }
30205
31023
  }
30206
31024
  const maxRecords = args.maxRecords ?? envInt2("KSQL_MAX_RECORDS") ?? profile.query?.maxRecords ?? 500;
31025
+ const recursiveCteMaxDepth = args.recursiveCteMaxDepth ?? envInt2("KSQL_RECURSIVE_CTE_MAX_DEPTH") ?? profile.query?.recursiveCteMaxDepth ?? 100;
31026
+ const recursiveCteMaxRows = args.recursiveCteMaxRows ?? envInt2("KSQL_RECURSIVE_CTE_MAX_ROWS") ?? profile.query?.recursiveCteMaxRows ?? 1e4;
31027
+ const recursiveCteMaxExpansions = args.recursiveCteMaxExpansions ?? envInt2("KSQL_RECURSIVE_CTE_MAX_EXPANSIONS") ?? profile.query?.recursiveCteMaxExpansions ?? 1e5;
31028
+ for (const [name, value] of [
31029
+ ["recursiveCteMaxDepth", recursiveCteMaxDepth],
31030
+ ["recursiveCteMaxRows", recursiveCteMaxRows],
31031
+ ["recursiveCteMaxExpansions", recursiveCteMaxExpansions]
31032
+ ]) {
31033
+ if (!Number.isSafeInteger(value) || value <= 0) {
31034
+ process.stderr.write(`ArgumentError: ${name} must be a positive safe integer.
31035
+ `);
31036
+ return 2;
31037
+ }
31038
+ }
30207
31039
  const fetchParallel = args.fetchParallel ?? envInt2("KSQL_FETCH_PARALLEL") ?? profile.query?.fetchParallel ?? 3;
30208
31040
  const onLimit = args.onLimit ?? envOnLimit2("KSQL_ON_LIMIT") ?? profile.query?.onLimit ?? "error";
30209
31041
  const timeout = args.timeout ?? envInt2("KSQL_TIMEOUT") ?? profile.query?.timeout ?? 3e4;
@@ -30560,7 +31392,10 @@ async function run() {
30560
31392
  Object.keys(args.importCsv).length > 0 || Object.keys(args.importJson).length > 0,
30561
31393
  dmlMaxRows,
30562
31394
  dmlMaxSubtableRows,
30563
- !dryRunUsesStaticTypedPlan
31395
+ !dryRunUsesStaticTypedPlan,
31396
+ recursiveCteMaxDepth,
31397
+ recursiveCteMaxRows,
31398
+ recursiveCteMaxExpansions
30564
31399
  );
30565
31400
  const out = [];
30566
31401
  const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
@@ -30650,6 +31485,9 @@ query=${label}`);
30650
31485
  tempTableMaxRows,
30651
31486
  timeoutMs: timeout,
30652
31487
  cursorMaxActive,
31488
+ recursiveCteMaxDepth,
31489
+ recursiveCteMaxRows,
31490
+ recursiveCteMaxExpansions,
30653
31491
  variables: args.variables,
30654
31492
  enableImport: importEnabled,
30655
31493
  importSource,
@@ -30701,7 +31539,10 @@ query=${label}`);
30701
31539
  enableImport: importEnabled,
30702
31540
  importSource,
30703
31541
  dmlMaxRows,
30704
- dmlMaxSubtableRows
31542
+ dmlMaxSubtableRows,
31543
+ recursiveCteMaxDepth,
31544
+ recursiveCteMaxRows,
31545
+ recursiveCteMaxExpansions
30705
31546
  }) : await execute(sql, client, {
30706
31547
  maxRecords,
30707
31548
  fetchParallel,
@@ -30709,6 +31550,9 @@ query=${label}`);
30709
31550
  confirm: isDmlStatement ? confirm : void 0,
30710
31551
  cacheContext,
30711
31552
  cursorMaxActive,
31553
+ recursiveCteMaxDepth,
31554
+ recursiveCteMaxRows,
31555
+ recursiveCteMaxExpansions,
30712
31556
  enableImport: importEnabled,
30713
31557
  importSource,
30714
31558
  supportsImportConfirmDetail: true,
@@ -30792,6 +31636,7 @@ if (isDirectCliRun()) {
30792
31636
  }
30793
31637
  // Annotate the CommonJS export names for ESM import in node:
30794
31638
  0 && (module.exports = {
31639
+ CLI_HELP_TEXT,
30795
31640
  CLI_IMPORT_SOURCE_REQUIRED_MESSAGE,
30796
31641
  HELP_TEXT,
30797
31642
  buildBatchDmlConfirmMessage,