@rex0220/kintone-sql-tools 3.62.0 → 3.63.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
@@ -76,6 +76,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
76
76
  ["INNER", "INNER" /* INNER */],
77
77
  ["LEFT", "LEFT" /* LEFT */],
78
78
  ["RIGHT", "RIGHT" /* RIGHT */],
79
+ ["CROSS", "CROSS" /* CROSS */],
79
80
  ["JOIN", "JOIN" /* JOIN */],
80
81
  ["ON", "ON" /* ON */],
81
82
  ["GROUP", "GROUP" /* GROUP */],
@@ -3016,6 +3017,13 @@ var Parser = class {
3016
3017
  const joinType = this.tryJoinType();
3017
3018
  if (joinType === null) break;
3018
3019
  const table = this.parseTableRef();
3020
+ if (joinType === "CROSS") {
3021
+ if (this.peek().kind === "ON" /* ON */) {
3022
+ throw new ParseError("CROSS JOIN \u306B ON \u53E5\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002", this.peek());
3023
+ }
3024
+ joins.push({ type: "CROSS", table, on: null });
3025
+ continue;
3026
+ }
3019
3027
  this.expect("ON" /* ON */);
3020
3028
  const on = this.parseJoinCondition();
3021
3029
  joins.push({ type: joinType, table, on });
@@ -3024,17 +3032,30 @@ var Parser = class {
3024
3032
  }
3025
3033
  tryJoinType() {
3026
3034
  if (this.consume("INNER" /* INNER */)) {
3035
+ if (this.peek().kind === "CROSS" /* CROSS */) {
3036
+ throw new ParseError("CROSS JOIN \u306B INNER \u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002", this.peek());
3037
+ }
3027
3038
  this.expect("JOIN" /* JOIN */);
3028
3039
  return "INNER";
3029
3040
  }
3030
3041
  if (this.consume("LEFT" /* LEFT */)) {
3042
+ if (this.peek().kind === "CROSS" /* CROSS */) {
3043
+ throw new ParseError("CROSS JOIN \u306B LEFT / RIGHT \u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002", this.peek());
3044
+ }
3031
3045
  this.expect("JOIN" /* JOIN */);
3032
3046
  return "LEFT";
3033
3047
  }
3034
3048
  if (this.consume("RIGHT" /* RIGHT */)) {
3049
+ if (this.peek().kind === "CROSS" /* CROSS */) {
3050
+ throw new ParseError("CROSS JOIN \u306B LEFT / RIGHT \u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002", this.peek());
3051
+ }
3035
3052
  this.expect("JOIN" /* JOIN */);
3036
3053
  return "RIGHT";
3037
3054
  }
3055
+ if (this.consume("CROSS" /* CROSS */)) {
3056
+ this.expect("JOIN" /* JOIN */);
3057
+ return "CROSS";
3058
+ }
3038
3059
  if (this.consume("JOIN" /* JOIN */)) {
3039
3060
  return "INNER";
3040
3061
  }
@@ -4584,6 +4605,8 @@ var Parser = class {
4584
4605
  this.advance();
4585
4606
  return tok.value;
4586
4607
  }
4608
+ const digitPrefixed = this.tryParseDigitPrefixedIdentifier();
4609
+ if (digitPrefixed !== null) return digitPrefixed;
4587
4610
  throw new ParseError(
4588
4611
  "\u30D5\u30A3\u30FC\u30EB\u30C9\u540D\u307E\u305F\u306F\u30C6\u30FC\u30D6\u30EB\u540D\u304C\u5FC5\u8981\u3067\u3059",
4589
4612
  tok
@@ -4597,11 +4620,24 @@ var Parser = class {
4597
4620
  this.advance();
4598
4621
  return tok.value;
4599
4622
  }
4623
+ const digitPrefixed = this.tryParseDigitPrefixedIdentifier();
4624
+ if (digitPrefixed !== null) return digitPrefixed;
4600
4625
  throw new ParseError(
4601
4626
  "\u30D5\u30A3\u30FC\u30EB\u30C9\u540D\u307E\u305F\u306F\u30C6\u30FC\u30D6\u30EB\u540D\u304C\u5FC5\u8981\u3067\u3059",
4602
4627
  tok
4603
4628
  );
4604
4629
  }
4630
+ /** `0埋め` のように数字から始まる日本語識別子を、空白なしの場合だけ読む。 */
4631
+ tryParseDigitPrefixedIdentifier() {
4632
+ const first = this.peek();
4633
+ const second = this.peekAt(1);
4634
+ if (first.kind !== "NUMBER" /* NUMBER */ || !/^\d+$/.test(first.value) || second.kind !== "IDENT" /* IDENT */ || second.pos !== first.pos + first.value.length) {
4635
+ return null;
4636
+ }
4637
+ this.advance();
4638
+ this.advance();
4639
+ return first.value + second.value;
4640
+ }
4605
4641
  // DML の対象テーブル位置に一時テーブルが指定されていたら拒否する
4606
4642
  rejectTempTableDml() {
4607
4643
  const tok = this.peek();
@@ -4781,6 +4817,7 @@ function unionCompleteInputReasons(stmt) {
4781
4817
  }
4782
4818
  function selectCompleteInputReasons(stmt) {
4783
4819
  const reasons = /* @__PURE__ */ new Set();
4820
+ if (stmt.joins.some((join2) => join2.type === "CROSS")) reasons.add("CROSS_JOIN");
4784
4821
  const grouping = normalizeGroupingSpec(stmt);
4785
4822
  if (grouping.type === "GROUPING_SETS") reasons.add("GROUPING_SETS");
4786
4823
  if (grouping.type === "PLAIN") reasons.add("GROUP_BY");
@@ -6887,6 +6924,7 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan, sourceAware) {
6887
6924
  }
6888
6925
  }
6889
6926
  for (const join2 of stmt.joins) {
6927
+ if (join2.type === "CROSS") continue;
6890
6928
  addFieldRef(join2.on.left.field, join2.on.left.tableAlias, "where");
6891
6929
  addFieldRef(join2.on.right.field, join2.on.right.tableAlias, "where");
6892
6930
  }
@@ -7831,7 +7869,9 @@ function buildSingleTableKlikePushdownPlan(where, options = {}) {
7831
7869
  return { condition, relation, appliedKlikes, allKlikes: [...allKlikes] };
7832
7870
  }
7833
7871
  function buildKlikePushdownPlan(stmt, options = {}) {
7834
- const joinsAreSafeForKlike = stmt.joins.every((join2) => join2.type === "INNER");
7872
+ const joinsAreSafeForKlike = stmt.joins.every(
7873
+ (join2) => join2.type === "INNER" || join2.type === "CROSS"
7874
+ );
7835
7875
  const common = {
7836
7876
  allowKlike: joinsAreSafeForKlike,
7837
7877
  allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
@@ -7851,7 +7891,7 @@ function buildKlikePushdownPlan(stmt, options = {}) {
7851
7891
  const joinRelations = /* @__PURE__ */ new Map();
7852
7892
  if (stmt.where !== null) {
7853
7893
  for (const join2 of stmt.joins) {
7854
- if (join2.type !== "INNER" || !join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
7894
+ if (join2.type !== "INNER" && join2.type !== "CROSS" || !join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
7855
7895
  const extracted = extractSafePushdownPlan(stmt.where, {
7856
7896
  ...common,
7857
7897
  tableAlias: join2.table.alias,
@@ -8198,7 +8238,7 @@ function validateSelect(stmt) {
8198
8238
  validateNestedSelects(stmt);
8199
8239
  }
8200
8240
  function canDeferJoinWholeWhereKlikeValidation(stmt) {
8201
- return stmt.where !== null && serverOnlyFunctionOccurrencesInWhere(stmt.where).length > 0 && stmt.joins.length > 0 && stmt.joins.every((join2) => join2.type === "INNER") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
8241
+ return stmt.where !== null && serverOnlyFunctionOccurrencesInWhere(stmt.where).length > 0 && stmt.joins.length > 0 && stmt.joins.every((join2) => join2.type === "INNER" || join2.type === "CROSS") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
8202
8242
  (table) => table.alias !== null && table.cteName === null && !table.subtableCode
8203
8243
  );
8204
8244
  }
@@ -8417,7 +8457,8 @@ function assertStringFunctionArity(func, args) {
8417
8457
  }
8418
8458
 
8419
8459
  // src/core/generateSeries.ts
8420
- var GENERATE_SERIES_MAX_ROWS = 1e4;
8460
+ var GENERATED_ROW_MAX_ROWS = 1e4;
8461
+ var GENERATE_SERIES_MAX_ROWS = GENERATED_ROW_MAX_ROWS;
8421
8462
  var argumentError = (message) => new Error(`ArgumentError: ${message}`);
8422
8463
  var isVariable = (arg) => arg.type === "VARIABLE";
8423
8464
  var isResolvedVariable = (arg) => arg?.type === "STRING" && arg.fromVariable === true;
@@ -8453,19 +8494,26 @@ function dateFromOrdinal(ordinal) {
8453
8494
  }
8454
8495
  function parseDateStep(value) {
8455
8496
  const trimmed = value.trim();
8456
- const match = /^([+-]?\d+)\s+(day|days)$/i.exec(trimmed);
8497
+ const match = /^([+-]?\d+)\s+(day|days|month|months|year|years)$/i.exec(trimmed);
8457
8498
  if (!match) {
8499
+ if (/^\S+\s+(?:day|days|month|months|year|years)$/i.test(trimmed)) {
8500
+ throw argumentError("GENERATE_SERIES \u306E\u65E5\u4ED8 step \u306E\u4FC2\u6570\u306B\u306F\u5B89\u5168\u306A\u6574\u6570\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8501
+ }
8458
8502
  if (/^[+-]?\d+\s+\S+$/i.test(trimmed)) {
8459
- throw argumentError("GENERATE_SERIES \u306E\u65E5\u4ED8 step \u306F day \u307E\u305F\u306F days \u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\u3002");
8503
+ throw argumentError("GENERATE_SERIES \u306E\u65E5\u4ED8 step \u306F day\u3001days\u3001month\u3001months\u3001year\u3001years \u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\u3002");
8460
8504
  }
8461
- throw argumentError("GENERATE_SERIES \u306E step \u304C\u7CFB\u5217\u306E\u578B\u3068\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002\u6574\u6570\u7CFB\u5217\u306B\u306F\u6574\u6570\u3001DATE \u7CFB\u5217\u306B\u306F day \u5358\u4F4D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8505
+ throw argumentError("GENERATE_SERIES \u306E step \u304C\u7CFB\u5217\u306E\u578B\u3068\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002\u6574\u6570\u7CFB\u5217\u306B\u306F\u6574\u6570\u3001DATE \u7CFB\u5217\u306B\u306F day\u3001month\u3001year \u5358\u4F4D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8462
8506
  }
8463
- const step = Number(match[1]);
8464
- if (!Number.isSafeInteger(step)) {
8465
- throw argumentError("GENERATE_SERIES \u306E\u6570\u5024\u7CFB\u5217\u306F\u6574\u6570\u306E start\u3001stop\u3001step \u306E\u307F\u3092\u53D7\u3051\u4ED8\u3051\u307E\u3059\u3002");
8507
+ const coefficient = Number(match[1]);
8508
+ if (!Number.isSafeInteger(coefficient)) {
8509
+ throw argumentError("GENERATE_SERIES \u306E\u65E5\u4ED8 step \u306E\u4FC2\u6570\u306B\u306F\u5B89\u5168\u306A\u6574\u6570\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8466
8510
  }
8467
- if (step === 0) throw argumentError("GENERATE_SERIES \u306E\u65E5\u4ED8 step \u306B 0 day \u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002");
8468
- return step;
8511
+ const rawUnit = match[2].toLowerCase();
8512
+ const unit = rawUnit.startsWith("month") ? "MONTH" : rawUnit.startsWith("year") ? "YEAR" : "DAY";
8513
+ if (coefficient === 0) {
8514
+ throw argumentError(`GENERATE_SERIES \u306E\u65E5\u4ED8 step \u306B 0 ${unit.toLowerCase()} \u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002`);
8515
+ }
8516
+ return { coefficient, unit };
8469
8517
  }
8470
8518
  function integerValue(value) {
8471
8519
  if (typeof value === "number") return Number.isSafeInteger(value) ? value : null;
@@ -8493,6 +8541,46 @@ function countRows(start, stop, step) {
8493
8541
  const distance = step > 0 ? BigInt(stop) - BigInt(start) : BigInt(start) - BigInt(stop);
8494
8542
  return Number(distance / BigInt(Math.abs(step)) + 1n);
8495
8543
  }
8544
+ function monthIndex(parts) {
8545
+ return parts.year * 12 + parts.month - 1;
8546
+ }
8547
+ function validateDateAnchor(start, unit) {
8548
+ if (unit === "MONTH" && start.day !== 1) {
8549
+ throw argumentError("GENERATE_SERIES \u306E month step \u3067\u306F start \u306B\u6708\u521D\uFF08YYYY-MM-01\uFF09\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8550
+ }
8551
+ if (unit === "YEAR" && (start.month !== 1 || start.day !== 1)) {
8552
+ throw argumentError("GENERATE_SERIES \u306E year step \u3067\u306F start \u306B\u5E74\u521D\uFF08YYYY-01-01\uFF09\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8553
+ }
8554
+ }
8555
+ function countDateRows(start, stop, step) {
8556
+ const startOrdinal = dateOrdinal(
8557
+ `${String(start.year).padStart(4, "0")}-${String(start.month).padStart(2, "0")}-${String(start.day).padStart(2, "0")}`
8558
+ );
8559
+ const stopOrdinal = dateOrdinal(
8560
+ `${String(stop.year).padStart(4, "0")}-${String(stop.month).padStart(2, "0")}-${String(stop.day).padStart(2, "0")}`
8561
+ );
8562
+ if (step.unit === "DAY") return countRows(startOrdinal, stopOrdinal, step.coefficient);
8563
+ if (startOrdinal === stopOrdinal) return 1;
8564
+ if (startOrdinal < stopOrdinal && step.coefficient < 0 || startOrdinal > stopOrdinal && step.coefficient > 0) return 0;
8565
+ if (step.unit === "MONTH") {
8566
+ const boundary2 = monthIndex(stop) + (step.coefficient < 0 && stop.day !== 1 ? 1 : 0);
8567
+ return countRows(monthIndex(start), boundary2, step.coefficient);
8568
+ }
8569
+ const stopIsYearStart = stop.month === 1 && stop.day === 1;
8570
+ const boundary = stop.year + (step.coefficient < 0 && !stopIsYearStart ? 1 : 0);
8571
+ return countRows(start.year, boundary, step.coefficient);
8572
+ }
8573
+ function dateFromParts(year, month, day) {
8574
+ if (year < 1 || year > 9999) {
8575
+ throw argumentError("GENERATE_SERIES \u306E\u65E5\u4ED8\u5F15\u6570\u306B\u306F\u5B9F\u5728\u3059\u308B YYYY-MM-DD \u5F62\u5F0F\u306E DATE \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8576
+ }
8577
+ return `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
8578
+ }
8579
+ function dateFromMonthIndex(index) {
8580
+ const year = Math.floor(index / 12);
8581
+ const month = index - year * 12 + 1;
8582
+ return dateFromParts(year, month, 1);
8583
+ }
8496
8584
  function planResolved(stmt) {
8497
8585
  if (stmt.args.length < 2 || stmt.args.length > 3) {
8498
8586
  throw argumentError("GENERATE_SERIES \u306F start\u3001stop \u3068\u7701\u7565\u53EF\u80FD\u306A step \u306E2\u500B\u307E\u305F\u306F3\u500B\u306E\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u3059\u3002");
@@ -8518,12 +8606,20 @@ function planResolved(stmt) {
8518
8606
  throw argumentError("GENERATE_SERIES \u306E\u65E5\u4ED8\u5F15\u6570\u306B\u306F\u5B9F\u5728\u3059\u308B YYYY-MM-DD \u5F62\u5F0F\u306E DATE \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8519
8607
  }
8520
8608
  if (stepRaw !== void 0 && typeof stepRaw !== "string") {
8521
- throw argumentError("GENERATE_SERIES \u306E step \u304C\u7CFB\u5217\u306E\u578B\u3068\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002\u6574\u6570\u7CFB\u5217\u306B\u306F\u6574\u6570\u3001DATE \u7CFB\u5217\u306B\u306F day \u5358\u4F4D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8609
+ throw argumentError("GENERATE_SERIES \u306E step \u304C\u7CFB\u5217\u306E\u578B\u3068\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002\u6574\u6570\u7CFB\u5217\u306B\u306F\u6574\u6570\u3001DATE \u7CFB\u5217\u306B\u306F day\u3001month\u3001year \u5358\u4F4D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8522
8610
  }
8523
- const step2 = stepRaw === void 0 ? 1 : parseDateStep(stepRaw);
8611
+ const dateStep = stepRaw === void 0 ? { coefficient: 1, unit: "DAY" } : parseDateStep(stepRaw);
8612
+ validateDateAnchor(startDate, dateStep.unit);
8524
8613
  const start2 = startRaw;
8525
8614
  const stop2 = stopRaw;
8526
- return { kind: "DATE", start: start2, stop: stop2, step: step2, rowCount: countRows(dateOrdinal(start2), dateOrdinal(stop2), step2) };
8615
+ return {
8616
+ kind: "DATE",
8617
+ start: start2,
8618
+ stop: stop2,
8619
+ step: dateStep.coefficient,
8620
+ dateUnit: dateStep.unit,
8621
+ rowCount: countDateRows(startDate, stopDate, dateStep)
8622
+ };
8527
8623
  }
8528
8624
  const startInteger = startArg.type === "NUMBER" ? integerNumberLiteral(startArg) : isResolvedVariable(startArg) ? integerValue(startRaw) : null;
8529
8625
  const stopInteger = stopArg.type === "NUMBER" ? integerNumberLiteral(stopArg) : isResolvedVariable(stopArg) ? integerValue(stopRaw) : null;
@@ -8538,7 +8634,7 @@ function planResolved(stmt) {
8538
8634
  if (stepArg?.type === "NUMBER" || isResolvedVariable(stepArg)) {
8539
8635
  throw argumentError("GENERATE_SERIES \u306E\u6570\u5024\u7CFB\u5217\u306F\u6574\u6570\u306E start\u3001stop\u3001step \u306E\u307F\u3092\u53D7\u3051\u4ED8\u3051\u307E\u3059\u3002");
8540
8636
  }
8541
- throw argumentError("GENERATE_SERIES \u306E step \u304C\u7CFB\u5217\u306E\u578B\u3068\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002\u6574\u6570\u7CFB\u5217\u306B\u306F\u6574\u6570\u3001DATE \u7CFB\u5217\u306B\u306F day \u5358\u4F4D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8637
+ throw argumentError("GENERATE_SERIES \u306E step \u304C\u7CFB\u5217\u306E\u578B\u3068\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002\u6574\u6570\u7CFB\u5217\u306B\u306F\u6574\u6570\u3001DATE \u7CFB\u5217\u306B\u306F day\u3001month\u3001year \u5358\u4F4D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
8542
8638
  }
8543
8639
  const start = startInteger;
8544
8640
  const stop = stopInteger;
@@ -8574,7 +8670,12 @@ function validateGenerateSeriesStatement(stmt) {
8574
8670
  if (value === null) throw argumentError("GENERATE_SERIES \u306E\u6570\u5024\u7CFB\u5217\u306F\u6574\u6570\u306E start\u3001stop\u3001step \u306E\u307F\u3092\u53D7\u3051\u4ED8\u3051\u307E\u3059\u3002");
8575
8671
  if (value === 0) throw argumentError("GENERATE_SERIES \u306E step \u306B 0 \u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002");
8576
8672
  } else if (step?.type === "STRING") {
8577
- parseDateStep(step.value);
8673
+ const dateStep = parseDateStep(step.value);
8674
+ const start = stmt.args[0];
8675
+ if (start?.type === "STRING") {
8676
+ const parts = dateParts(start.value);
8677
+ if (parts) validateDateAnchor(parts, dateStep.unit);
8678
+ }
8578
8679
  }
8579
8680
  return null;
8580
8681
  }
@@ -8629,8 +8730,22 @@ function resolveGenerateSeries(stmt) {
8629
8730
  }
8630
8731
  }
8631
8732
  } else {
8632
- const startOrdinal = dateOrdinal(plan.start);
8633
- for (let index = 0; index < plan.rowCount; index++) values.push(dateFromOrdinal(startOrdinal + index * plan.step));
8733
+ const start = dateParts(plan.start);
8734
+ if (plan.dateUnit === "MONTH") {
8735
+ const anchor = monthIndex(start);
8736
+ for (let index = 0; index < plan.rowCount; index++) {
8737
+ values.push(dateFromMonthIndex(anchor + index * plan.step));
8738
+ }
8739
+ } else if (plan.dateUnit === "YEAR") {
8740
+ for (let index = 0; index < plan.rowCount; index++) {
8741
+ values.push(dateFromParts(start.year + index * plan.step, 1, 1));
8742
+ }
8743
+ } else {
8744
+ const startOrdinal = dateOrdinal(plan.start);
8745
+ for (let index = 0; index < plan.rowCount; index++) {
8746
+ values.push(dateFromOrdinal(startOrdinal + index * plan.step));
8747
+ }
8748
+ }
8634
8749
  }
8635
8750
  return { ...plan, values };
8636
8751
  }
@@ -12774,6 +12889,19 @@ function planJoinKeyPrefilter(input) {
12774
12889
  return { kind: "RANGE", min, max, relation: "superset" };
12775
12890
  }
12776
12891
 
12892
+ // src/core/optimization/crossJoinRowPlan.ts
12893
+ var CROSS_JOIN_MAX_ROWS = GENERATED_ROW_MAX_ROWS;
12894
+ function planCrossJoinRows(leftRows, rightRows, limit = CROSS_JOIN_MAX_ROWS) {
12895
+ const outputRows = leftRows === 0 || rightRows === 0 ? 0 : leftRows * rightRows;
12896
+ return {
12897
+ leftRows,
12898
+ rightRows,
12899
+ outputRows,
12900
+ limit,
12901
+ allowed: outputRows <= limit
12902
+ };
12903
+ }
12904
+
12777
12905
  // src/core/explainMetadata.ts
12778
12906
  function buildGroupingExplainMetadata(statement, canonicalItemCount) {
12779
12907
  const grouping = normalizeGroupingSpec(statement);
@@ -12814,11 +12942,30 @@ function valueNeedsFieldMetadata(value) {
12814
12942
  }
12815
12943
  function selectNeedsOwnMetadata(statement) {
12816
12944
  return whereNeedsFieldMetadata(statement.where) || statement.groupBy.length > 0 || normalizeGroupingSpec(statement).type === "GROUPING_SETS" || statement.orderBy.length > 0 || statement.joins.some(
12817
- (join2) => join2.table.appId > 0 && join2.table.cteName === null && (join2.on.left.field !== "$id" || join2.on.right.field !== "$id")
12945
+ (join2) => join2.type !== "CROSS" && join2.table.appId > 0 && join2.table.cteName === null && (join2.on.left.field !== "$id" || join2.on.right.field !== "$id")
12818
12946
  ) || statement.columns.some(
12819
12947
  (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
12820
12948
  );
12821
12949
  }
12950
+ function cteQueriesContainPhysicalSelect(ctes) {
12951
+ if (!Array.isArray(ctes)) return false;
12952
+ const seen = /* @__PURE__ */ new Set();
12953
+ const visit = (node) => {
12954
+ if (node === null || typeof node !== "object") return false;
12955
+ if (seen.has(node)) return false;
12956
+ seen.add(node);
12957
+ if (Array.isArray(node)) return node.some(visit);
12958
+ const item = node;
12959
+ if (item["type"] === "SELECT") {
12960
+ const select = node;
12961
+ if ([select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.appId > 0 && table.cteName === null)) {
12962
+ return true;
12963
+ }
12964
+ }
12965
+ return Object.values(item).some(visit);
12966
+ };
12967
+ return ctes.some((cte) => visit(cte?.query));
12968
+ }
12822
12969
  function explainNeedsAppMetadata(statement) {
12823
12970
  const seen = /* @__PURE__ */ new Set();
12824
12971
  const visit = (node) => {
@@ -12828,6 +12975,9 @@ function explainNeedsAppMetadata(statement) {
12828
12975
  if (Array.isArray(node)) return node.some(visit);
12829
12976
  const item = node;
12830
12977
  if (item["type"] === "VALIDATE") return true;
12978
+ if (item["type"] === "WITH" && cteQueriesContainPhysicalSelect(item["ctes"])) {
12979
+ return true;
12980
+ }
12831
12981
  if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
12832
12982
  return true;
12833
12983
  }
@@ -14051,6 +14201,19 @@ function flatten(record, alias) {
14051
14201
  return row;
14052
14202
  }
14053
14203
  function applyJoin(leftRows, rightRows, join2, columns = {}) {
14204
+ if (join2.type === "CROSS") {
14205
+ const plan = planCrossJoinRows(leftRows.length, rightRows.length);
14206
+ if (!plan.allowed) {
14207
+ throw new Error(
14208
+ `ArgumentError: CROSS JOIN \u306E\u751F\u6210\u4EF6\u6570 ${plan.outputRows} \u884C\uFF08\u5DE6 ${plan.leftRows} \u884C \xD7 \u53F3 ${plan.rightRows} \u884C\uFF09\u304C\u4E0A\u9650 ${plan.limit} \u884C\u3092\u8D85\u3048\u3066\u3044\u307E\u3059\u3002`
14209
+ );
14210
+ }
14211
+ const result2 = [];
14212
+ for (const lRow of leftRows) {
14213
+ for (const rRow of rightRows) result2.push({ ...lRow, ...rRow });
14214
+ }
14215
+ return result2;
14216
+ }
14054
14217
  const { on, type: joinType } = join2;
14055
14218
  const leftKey = on.left.tableAlias ? `${on.left.tableAlias}.${on.left.field}` : on.left.field;
14056
14219
  const rightKey = on.right.tableAlias ? `${on.right.tableAlias}.${on.right.field}` : on.right.field;
@@ -15741,7 +15904,7 @@ function allowRelativeDatePrefilterPlan(select, decomposition) {
15741
15904
  return decomposition.eligible === true && resolveSelectMode(select) === "FULL_SCAN" && select.orderMode !== "KINTONE_NATIVE" && select.from.cteName === null && !select.from.subtableCode && select.joins.length === 0;
15742
15905
  }
15743
15906
  function allowJoinServerFunctionPlan(select, plan) {
15744
- return select.where !== null && select.joins.length > 0 && select.joins.every((join2) => join2.type === "INNER") && [select.from, ...select.joins.map((join2) => join2.table)].every(
15907
+ return select.where !== null && select.joins.length > 0 && select.joins.every((join2) => join2.type === "INNER" || join2.type === "CROSS") && [select.from, ...select.joins.map((join2) => join2.table)].every(
15745
15908
  (table) => table.alias !== null && table.cteName === null && !table.subtableCode
15746
15909
  ) && isJoinServerFunctionFetchPlan(plan);
15747
15910
  }
@@ -19056,7 +19219,7 @@ function completeInputErrorPrefix(reasons) {
19056
19219
  const hasAggregateReason = aggregateSubjects.some(([reason]) => reasons.has(reason));
19057
19220
  const hasOrderReason = reasons.has("LOCAL_ORDER") || reasons.has("WINDOW_ORDER");
19058
19221
  const legacySubject = reasons.size === 1 && reasons.has("STATISTICAL_AGGREGATE") ? "\u7D71\u8A08\u96C6\u7D04\u306E\u6B63\u3057\u3044\u7D50\u679C" : reasons.size === 1 && reasons.has("GROUPING_SETS") ? "\u5C0F\u8A08\u30FB\u7DCF\u8A08\u306E\u6B63\u3057\u3044\u7D50\u679C" : reasons.size === 1 && reasons.has("AGGREGATE") ? "\u96C6\u8A08\u306E\u6B63\u3057\u3044\u7D50\u679C" : reasons.size === 1 && reasons.has("GROUP_BY") ? "\u30B0\u30EB\u30FC\u30D7\u96C6\u8A08\u306E\u6B63\u3057\u3044\u7D50\u679C" : reasons.size === 1 && reasons.has("DISTINCT") ? "DISTINCT \u306E\u6B63\u3057\u3044\u7D50\u679C" : reasons.has("GROUPING_SETS") ? "\u30AF\u30A8\u30EA\u306E\u6B63\u3057\u3044\u7D50\u679C" : "ORDER BY\u306E\u6B63\u3057\u3044\u7D50\u679C";
19059
- const subject = reasons.has("DML") || reasons.has("VALIDATE") ? legacySubject : hasAggregateReason && hasOrderReason ? "\u30AF\u30A8\u30EA\u306E\u6B63\u3057\u3044\u7D50\u679C" : hasAggregateReason ? aggregateSubjects.find(([reason]) => reasons.has(reason))[1] : "ORDER BY\u306E\u6B63\u3057\u3044\u7D50\u679C";
19222
+ const subject = reasons.size === 1 && reasons.has("CROSS_JOIN") ? "CROSS JOIN \u306E\u6B63\u3057\u3044\u7D50\u679C" : reasons.has("DML") || reasons.has("VALIDATE") ? legacySubject : hasAggregateReason && hasOrderReason ? "\u30AF\u30A8\u30EA\u306E\u6B63\u3057\u3044\u7D50\u679C" : hasAggregateReason ? aggregateSubjects.find(([reason]) => reasons.has(reason))[1] : "ORDER BY\u306E\u6B63\u3057\u3044\u7D50\u679C";
19060
19223
  return `${subject}\u306B\u306F\u5B8C\u5168\u306A\u5019\u88DC\u96C6\u5408\u304C\u5FC5\u8981\u3067\u3059\u3002complete input reason: ${reasonList}\u3002`;
19061
19224
  }
19062
19225
  function buildCompleteInputPolicy(stmt, options, orderPlan) {
@@ -19427,6 +19590,7 @@ async function validateB86SelectFieldCodes(stmt, client, cteCache, cacheContext)
19427
19590
  for (const alias of b86SourceAliases(schema.table)) sourceByAlias.set(alias, schema);
19428
19591
  }
19429
19592
  for (const join2 of stmt.joins) {
19593
+ if (join2.type === "CROSS") continue;
19430
19594
  for (const ref of [join2.on.left, join2.on.right]) {
19431
19595
  if (!ref.tableAlias) continue;
19432
19596
  const schema = sourceByAlias.get(ref.tableAlias);
@@ -19497,7 +19661,7 @@ function hasPushdownPlaceholder(where) {
19497
19661
  }
19498
19662
  var boundJoinRuntimePlans = /* @__PURE__ */ new WeakMap();
19499
19663
  function buildRuntimeJoinPushdownPlan(stmt, metadata) {
19500
- if (stmt.joins.length === 0 || stmt.where === null || stmt.joins.some((join2) => join2.type !== "INNER")) {
19664
+ if (stmt.joins.length === 0 || stmt.where === null || stmt.joins.some((join2) => join2.type !== "INNER" && join2.type !== "CROSS")) {
19501
19665
  return null;
19502
19666
  }
19503
19667
  const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
@@ -19556,7 +19720,7 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
19556
19720
  else candidatesByApp.set(appId, [candidate]);
19557
19721
  };
19558
19722
  addCandidate(stmt.from.appId, extractMainTypedPushdownCandidate(stmt));
19559
- const directInnerJoin = stmt.joins.length > 0 && stmt.where !== null && stmt.joins.every((join2) => join2.type === "INNER") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
19723
+ const directInnerJoin = stmt.joins.length > 0 && stmt.where !== null && stmt.joins.every((join2) => join2.type === "INNER" || join2.type === "CROSS") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
19560
19724
  (table) => table.alias !== null && table.cteName === null && !table.subtableCode
19561
19725
  );
19562
19726
  if (directInnerJoin) {
@@ -24273,6 +24437,7 @@ var explainJoinPushdownPlans = /* @__PURE__ */ new WeakMap();
24273
24437
  var explainPushdownPlans = /* @__PURE__ */ new WeakMap();
24274
24438
  var explainJoinKeyPrefilters = /* @__PURE__ */ new WeakMap();
24275
24439
  var explainChoiceEqualityRewrites = /* @__PURE__ */ new WeakMap();
24440
+ var explainCrossJoinSteps = /* @__PURE__ */ new WeakMap();
24276
24441
  var validateExplainInfo = /* @__PURE__ */ new WeakMap();
24277
24442
  var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
24278
24443
  async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4, relativeDatePlan, initialRelations) {
@@ -24304,6 +24469,54 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
24304
24469
  );
24305
24470
  const explainRelations = new Map(initialRelations ?? []);
24306
24471
  const staticExplainRelations = new Set(initialRelations?.keys() ?? []);
24472
+ const exactRelationRows = new Map(
24473
+ [...initialRelations ?? /* @__PURE__ */ new Map()].map(([name, relation]) => [name, relation.rows.length])
24474
+ );
24475
+ const staticSelectRows = /* @__PURE__ */ new WeakMap();
24476
+ const tableLabel = (table) => effectiveTableAlias(table) ?? (table.appId > 0 ? `APP${table.appId}` : "source");
24477
+ const tableExactRows = (table) => {
24478
+ if (table.cteName === NO_FROM_CTE_NAME) return 1;
24479
+ if (table.cteName !== null) return exactRelationRows.get(table.cteName) ?? null;
24480
+ return null;
24481
+ };
24482
+ const analyzeStaticSelectRows = (select) => {
24483
+ let currentRows = tableExactRows(select.from);
24484
+ let leftLabel = tableLabel(select.from);
24485
+ const steps = [];
24486
+ for (const join2 of select.joins) {
24487
+ const rightRows = tableExactRows(join2.table);
24488
+ const rightLabel = tableLabel(join2.table);
24489
+ if (join2.type === "CROSS") {
24490
+ const plan = currentRows !== null && rightRows !== null ? planCrossJoinRows(currentRows, rightRows) : null;
24491
+ steps.push({
24492
+ leftLabel,
24493
+ rightLabel,
24494
+ leftRows: currentRows,
24495
+ rightRows,
24496
+ plan,
24497
+ rightRuntimeLabel: join2.table.cteName === null ? `APP${join2.table.appId} fetched rows` : `${rightLabel} materialized rows`
24498
+ });
24499
+ currentRows = plan?.outputRows ?? null;
24500
+ leftLabel = `${leftLabel} \xD7 ${rightLabel}`;
24501
+ } else {
24502
+ currentRows = null;
24503
+ leftLabel = `${leftLabel} ${join2.type} JOIN ${rightLabel}`;
24504
+ }
24505
+ }
24506
+ if (steps.length > 0) explainCrossJoinSteps.set(select, steps);
24507
+ if (isConstantFalseWhere(select.where)) currentRows = 0;
24508
+ else if (select.where !== null) currentRows = null;
24509
+ const grouping = normalizeGroupingSpec(select);
24510
+ if (grouping.type !== "NONE" || select.distinct || select.having !== null || isAggregateQueryBlock(select) || select.columns.some((column) => column.type === "WINDOW_COL")) {
24511
+ currentRows = null;
24512
+ }
24513
+ if (currentRows !== null) {
24514
+ const offset = select.offset ?? 0;
24515
+ currentRows = Math.max(0, currentRows - offset);
24516
+ if (select.limit !== null) currentRows = Math.min(currentRows, select.limit);
24517
+ staticSelectRows.set(select, currentRows);
24518
+ }
24519
+ };
24307
24520
  const explainSourceColumns = async (select) => {
24308
24521
  const tables = [select.from, ...select.joins.map((join2) => join2.table)];
24309
24522
  if (tables.length > 1 && select.columns.some(
@@ -24392,10 +24605,15 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
24392
24605
  uniqueGeneratedColumn: cte.query.columnAlias
24393
24606
  });
24394
24607
  staticExplainRelations.add(cte.name);
24608
+ exactRelationRows.set(cte.name, generated.rows.length);
24395
24609
  continue;
24396
24610
  }
24397
24611
  const columns = await inferExplainRelationColumns(cte.query);
24398
24612
  explainRelations.set(cte.name, { rows: [], columns });
24613
+ if (cte.query.type === "SELECT") {
24614
+ const rowCount = staticSelectRows.get(cte.query);
24615
+ if (rowCount !== void 0) exactRelationRows.set(cte.name, rowCount);
24616
+ }
24399
24617
  }
24400
24618
  await preflightExplainRelations(withStatement.query);
24401
24619
  return;
@@ -24407,6 +24625,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
24407
24625
  }
24408
24626
  if (typed["type"] === "SELECT") {
24409
24627
  const select = node;
24628
+ analyzeStaticSelectRows(select);
24410
24629
  for (const column of select.columns) {
24411
24630
  if (column.type === "SCALAR_SUBQUERY_COL") await preflightExplainRelations(column.query);
24412
24631
  }
@@ -25440,6 +25659,23 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
25440
25659
  );
25441
25660
  if (label) lines.push(label);
25442
25661
  lines.push(` mode: ${mode}`);
25662
+ for (const step of explainCrossJoinSteps.get(stmt) ?? []) {
25663
+ lines.push(` cross join: ${step.leftLabel} \xD7 ${step.rightLabel}`);
25664
+ lines.push(` left rows: ${step.leftRows ?? "runtime (left intermediate rows)"}`);
25665
+ lines.push(` right rows: ${step.rightRows ?? `runtime (${step.rightRuntimeLabel})`}`);
25666
+ if (step.plan) {
25667
+ lines.push(` rows: ${step.plan.outputRows}`);
25668
+ lines.push(` row guard: ${step.plan.outputRows} / ${step.plan.limit}`);
25669
+ lines.push(" guard timing: before row materialization");
25670
+ } else {
25671
+ const left = step.leftRows === null ? "left rows" : String(step.leftRows);
25672
+ const right = step.rightRows === null ? "right rows" : String(step.rightRows);
25673
+ lines.push(` rows: runtime (${left} \xD7 ${right})`);
25674
+ lines.push(` row guard: runtime checked / ${CROSS_JOIN_MAX_ROWS}`);
25675
+ lines.push(" guard timing: after complete source fetch, before row materialization");
25676
+ }
25677
+ lines.push(" records API: none");
25678
+ }
25443
25679
  for (const rewrite of explainChoiceEqualityRewrites.get(stmt) ?? choiceEqualityRewritesBySelect.get(stmt) ?? []) {
25444
25680
  lines.push(formatChoiceEqualityRewrite(rewrite));
25445
25681
  }
@@ -25604,7 +25840,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
25604
25840
  }
25605
25841
  }
25606
25842
  } else {
25607
- const reason = stmt.joins.some((join2) => join2.type !== "INNER") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
25843
+ const reason = stmt.joins.some((join2) => join2.type !== "INNER" && join2.type !== "CROSS") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
25608
25844
  (table) => table.cteName !== null || Boolean(table.subtableCode)
25609
25845
  ) ? "SOURCE_KIND" : "PLAN_NOT_APPLICABLE";
25610
25846
  lines.push(" join pushdown plan: not applied (join key/WHERE prefilters are reported per source below)");
@@ -25756,7 +25992,67 @@ function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans, colle
25756
25992
  });
25757
25993
  return lines;
25758
25994
  }
25995
+ function populateWithCrossJoinExplain(stmt) {
25996
+ const exactRows = /* @__PURE__ */ new Map();
25997
+ const labelFor = (table) => effectiveTableAlias(table) ?? (table.appId > 0 ? `APP${table.appId}` : "source");
25998
+ const rowsFor = (table) => {
25999
+ if (table.cteName === NO_FROM_CTE_NAME) return 1;
26000
+ return table.cteName === null ? null : exactRows.get(table.cteName) ?? null;
26001
+ };
26002
+ const analyze = (select) => {
26003
+ let current = rowsFor(select.from);
26004
+ let leftLabel = labelFor(select.from);
26005
+ const steps = [];
26006
+ for (const join2 of select.joins) {
26007
+ const right = rowsFor(join2.table);
26008
+ const rightLabel = labelFor(join2.table);
26009
+ if (join2.type === "CROSS") {
26010
+ const plan = current !== null && right !== null ? planCrossJoinRows(current, right) : null;
26011
+ steps.push({
26012
+ leftLabel,
26013
+ rightLabel,
26014
+ leftRows: current,
26015
+ rightRows: right,
26016
+ plan,
26017
+ rightRuntimeLabel: join2.table.cteName === null ? `APP${join2.table.appId} fetched rows` : `${rightLabel} materialized rows`
26018
+ });
26019
+ current = plan?.outputRows ?? null;
26020
+ leftLabel = `${leftLabel} \xD7 ${rightLabel}`;
26021
+ } else {
26022
+ current = null;
26023
+ leftLabel = `${leftLabel} ${join2.type} JOIN ${rightLabel}`;
26024
+ }
26025
+ }
26026
+ if (steps.length > 0) explainCrossJoinSteps.set(select, steps);
26027
+ if (isConstantFalseWhere(select.where)) current = 0;
26028
+ else if (select.where !== null) current = null;
26029
+ if (normalizeGroupingSpec(select).type !== "NONE" || select.distinct || select.having !== null || isAggregateQueryBlock(select) || select.columns.some((column) => column.type === "WINDOW_COL")) return null;
26030
+ if (current === null) return null;
26031
+ current = Math.max(0, current - (select.offset ?? 0));
26032
+ return select.limit === null ? current : Math.min(current, select.limit);
26033
+ };
26034
+ const analyzeQuery = (query) => {
26035
+ if (query.type === "SELECT") {
26036
+ analyze(query);
26037
+ return;
26038
+ }
26039
+ analyzeQuery(query.left);
26040
+ analyze(query.right);
26041
+ };
26042
+ for (const cte of stmt.ctes) {
26043
+ if (cte.query.type === "GENERATE_SERIES") {
26044
+ exactRows.set(cte.name, resolveGenerateSeries(cte.query).rowCount);
26045
+ } else if (cte.query.type === "SELECT") {
26046
+ const rows = analyze(cte.query);
26047
+ if (rows !== null) exactRows.set(cte.name, rows);
26048
+ } else if (cte.query.type === "UNION") {
26049
+ analyzeQuery(cte.query);
26050
+ }
26051
+ }
26052
+ analyzeQuery(stmt.query);
26053
+ }
25759
26054
  function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
26055
+ populateWithCrossJoinExplain(stmt);
25760
26056
  const lines = [];
25761
26057
  for (const cte of stmt.ctes) {
25762
26058
  if (cte.query.type === "SELECT") {
@@ -25774,7 +26070,7 @@ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collec
25774
26070
  lines.push("");
25775
26071
  } else if (cte.query.type === "GENERATE_SERIES") {
25776
26072
  const series = resolveGenerateSeries(cte.query);
25777
- const step = series.kind === "DATE" ? `${series.step} ${Math.abs(series.step) === 1 ? "day" : "days"}` : String(series.step);
26073
+ const step = series.kind === "DATE" ? `${series.step} ${String(series.dateUnit ?? "DAY").toLowerCase()}${Math.abs(series.step) === 1 ? "" : "s"}` : String(series.step);
25778
26074
  lines.push(
25779
26075
  `[cte: ${cte.name}]`,
25780
26076
  " source: GENERATE_SERIES",
@@ -25825,7 +26121,9 @@ function collectFullScanReasons(stmt) {
25825
26121
  const r = [];
25826
26122
  if (stmt.from.subtableCode || stmt.joins.some((j) => j.table.subtableCode))
25827
26123
  r.push("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB");
25828
- if (stmt.joins.length > 0)
26124
+ if (stmt.joins.some((join2) => join2.type === "CROSS"))
26125
+ r.push("CROSS JOIN \u3042\u308A");
26126
+ if (stmt.joins.some((join2) => join2.type !== "CROSS"))
25829
26127
  r.push("JOIN \u3042\u308A");
25830
26128
  const grouping = normalizeGroupingSpec(stmt);
25831
26129
  if (grouping.type === "PLAIN")
@@ -28906,14 +29204,39 @@ function createDryRunClient() {
28906
29204
  function hasStaticTypedPushdownCandidate(statement) {
28907
29205
  if (statement === null || typeof statement !== "object") return false;
28908
29206
  const node = statement;
28909
- if (node["type"] === "WITH") return hasStaticTypedPushdownCandidate(node["query"]);
29207
+ if (node["type"] === "WITH") {
29208
+ const query = node["query"];
29209
+ const containsCross = (value) => {
29210
+ if (Array.isArray(value)) return value.some(containsCross);
29211
+ if (value === null || typeof value !== "object") return false;
29212
+ const item = value;
29213
+ if (item["type"] === "SELECT") {
29214
+ if (Array.isArray(item["joins"]) && item["joins"].some(
29215
+ (join2) => typeof join2 === "object" && join2 !== null && join2["type"] === "CROSS"
29216
+ )) return true;
29217
+ }
29218
+ return Object.values(item).some(containsCross);
29219
+ };
29220
+ const hasPhysicalCte = Array.isArray(node["ctes"]) && node["ctes"].some((cte) => {
29221
+ if (typeof cte !== "object" || cte === null) return false;
29222
+ const cteQuery = cte["query"];
29223
+ if (typeof cteQuery !== "object" || cteQuery === null) return false;
29224
+ const from = cteQuery["from"];
29225
+ return cteQuery["type"] === "SELECT" && typeof from === "object" && from !== null && from["cteName"] === null;
29226
+ });
29227
+ return (containsCross(node["ctes"]) || containsCross(query)) && hasPhysicalCte || hasStaticTypedPushdownCandidate(query);
29228
+ }
28910
29229
  if (node["type"] !== "SELECT") return false;
28911
29230
  const select = statement;
28912
- if (!select.where || !Array.isArray(select.joins) || select.joins.length === 0) return false;
29231
+ if (!Array.isArray(select.joins) || select.joins.length === 0) return false;
28913
29232
  if (![select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) return false;
29233
+ if (select.joins.some((join2) => join2.type === "CROSS" && join2.table.cteName === null)) {
29234
+ return true;
29235
+ }
29236
+ if (!select.where) return false;
28914
29237
  const where = select.where;
28915
29238
  return select.joins.some(
28916
- (join2) => join2.type === "INNER" && join2.table?.alias && join2.table?.cteName === null && !join2.table?.subtableCode && extractTypedPushdownCandidates(where, { tableAlias: join2.table.alias }) !== null
29239
+ (join2) => (join2.type === "INNER" || join2.type === "CROSS") && join2.table?.alias && join2.table?.cteName === null && !join2.table?.subtableCode && extractTypedPushdownCandidates(where, { tableAlias: join2.table.alias }) !== null
28917
29240
  );
28918
29241
  }
28919
29242
  async function runDiagnosticRecordGet(params) {
@@ -29987,7 +30310,7 @@ async function run() {
29987
30310
  Object.keys(args.importCsv).length > 0 || Object.keys(args.importJson).length > 0,
29988
30311
  dmlMaxRows,
29989
30312
  dmlMaxSubtableRows,
29990
- false
30313
+ !dryRunUsesStaticTypedPlan
29991
30314
  );
29992
30315
  const out = [];
29993
30316
  const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;