@rex0220/kintone-sql-tools 3.62.0 → 3.64.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 +658 -85
- package/dist-engine/index.cjs +14 -14
- package/dist-engine/index.mjs +14 -14
- package/dist-engine/ksql-engine.umd.js +14 -14
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +52 -28
- package/dist-engine/meta/esm.json +52 -28
- package/dist-engine/meta/umd.json +52 -28
- package/dist-mcp/ksql-mcp.js +636 -92
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
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(
|
|
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
|
|
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
|
|
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
|
|
8464
|
-
if (!Number.isSafeInteger(
|
|
8465
|
-
throw argumentError("GENERATE_SERIES \u306E\
|
|
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
|
-
|
|
8468
|
-
|
|
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
|
|
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 {
|
|
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
|
|
8633
|
-
|
|
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(
|
|
@@ -24384,6 +24597,10 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24384
24597
|
for (const cte of withStatement.ctes) {
|
|
24385
24598
|
await preflightExplainRelations(cte.query);
|
|
24386
24599
|
if (cte.query.type === "GENERATE_SERIES") {
|
|
24600
|
+
if (cte.query.args.some((arg) => arg.type === "VARIABLE")) {
|
|
24601
|
+
explainRelations.set(cte.name, { rows: [], columns: [cte.query.columnAlias] });
|
|
24602
|
+
continue;
|
|
24603
|
+
}
|
|
24387
24604
|
const generated = executeGenerateSeries(cte.query);
|
|
24388
24605
|
explainRelations.set(cte.name, {
|
|
24389
24606
|
rows: generated.rows,
|
|
@@ -24392,10 +24609,15 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24392
24609
|
uniqueGeneratedColumn: cte.query.columnAlias
|
|
24393
24610
|
});
|
|
24394
24611
|
staticExplainRelations.add(cte.name);
|
|
24612
|
+
exactRelationRows.set(cte.name, generated.rows.length);
|
|
24395
24613
|
continue;
|
|
24396
24614
|
}
|
|
24397
24615
|
const columns = await inferExplainRelationColumns(cte.query);
|
|
24398
24616
|
explainRelations.set(cte.name, { rows: [], columns });
|
|
24617
|
+
if (cte.query.type === "SELECT") {
|
|
24618
|
+
const rowCount = staticSelectRows.get(cte.query);
|
|
24619
|
+
if (rowCount !== void 0) exactRelationRows.set(cte.name, rowCount);
|
|
24620
|
+
}
|
|
24399
24621
|
}
|
|
24400
24622
|
await preflightExplainRelations(withStatement.query);
|
|
24401
24623
|
return;
|
|
@@ -24407,6 +24629,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24407
24629
|
}
|
|
24408
24630
|
if (typed["type"] === "SELECT") {
|
|
24409
24631
|
const select = node;
|
|
24632
|
+
analyzeStaticSelectRows(select);
|
|
24410
24633
|
for (const column of select.columns) {
|
|
24411
24634
|
if (column.type === "SCALAR_SUBQUERY_COL") await preflightExplainRelations(column.query);
|
|
24412
24635
|
}
|
|
@@ -24418,13 +24641,19 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
24418
24641
|
cacheContext,
|
|
24419
24642
|
explainRelations
|
|
24420
24643
|
);
|
|
24421
|
-
const
|
|
24422
|
-
|
|
24423
|
-
|
|
24424
|
-
cacheContext,
|
|
24425
|
-
explainRelations
|
|
24644
|
+
const sources = [select.from, ...select.joins.map((join2) => join2.table)];
|
|
24645
|
+
const hasUnavailableMaterializedSource = sources.some(
|
|
24646
|
+
(source) => source.cteName !== null && !explainRelations.has(source.cteName)
|
|
24426
24647
|
);
|
|
24427
|
-
if (
|
|
24648
|
+
if (!hasUnavailableMaterializedSource) {
|
|
24649
|
+
const plainPlan = await buildRuntimePlainGroupByPlan(
|
|
24650
|
+
select,
|
|
24651
|
+
tracedClient,
|
|
24652
|
+
cacheContext,
|
|
24653
|
+
explainRelations
|
|
24654
|
+
);
|
|
24655
|
+
if (plainPlan) plainGroupByPlans.set(select, plainPlan);
|
|
24656
|
+
}
|
|
24428
24657
|
return;
|
|
24429
24658
|
}
|
|
24430
24659
|
for (const child of Object.values(typed)) await preflightExplainRelations(child);
|
|
@@ -24953,6 +25182,106 @@ function serverFunctionClientEvaluationLabel(leaves) {
|
|
|
24953
25182
|
(leaf) => leaf.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(leaf.right.name)
|
|
24954
25183
|
) ? "relative date client evaluations" : "kintone function client evaluations";
|
|
24955
25184
|
}
|
|
25185
|
+
var explainSeriesBindings = /* @__PURE__ */ new WeakMap();
|
|
25186
|
+
function resolveExplainGenerateSeriesDefaults(node, literalDefaults) {
|
|
25187
|
+
if (Array.isArray(node)) {
|
|
25188
|
+
return node.map((value) => resolveExplainGenerateSeriesDefaults(value, literalDefaults));
|
|
25189
|
+
}
|
|
25190
|
+
if (node === null || typeof node !== "object") return node;
|
|
25191
|
+
const object = node;
|
|
25192
|
+
if (object["type"] === "GENERATE_SERIES") {
|
|
25193
|
+
const variableNames = /* @__PURE__ */ new Map();
|
|
25194
|
+
const defaultBoundIndexes = /* @__PURE__ */ new Set();
|
|
25195
|
+
const args = object["args"].map((arg, index) => {
|
|
25196
|
+
if (arg.type !== "STRING" || arg.fromVariable !== true || !arg.value.startsWith("@")) return arg;
|
|
25197
|
+
const name = arg.value.slice(1);
|
|
25198
|
+
variableNames.set(index, name);
|
|
25199
|
+
const defaultValue = literalDefaults.get(name);
|
|
25200
|
+
if (defaultValue === void 0) return { type: "VARIABLE", name };
|
|
25201
|
+
defaultBoundIndexes.add(index);
|
|
25202
|
+
return { type: "STRING", value: defaultValue, fromVariable: true };
|
|
25203
|
+
});
|
|
25204
|
+
const resolved = { ...object, args };
|
|
25205
|
+
explainSeriesBindings.set(resolved, { variableNames, defaultBoundIndexes });
|
|
25206
|
+
return resolved;
|
|
25207
|
+
}
|
|
25208
|
+
return Object.fromEntries(Object.entries(object).map(([key, value]) => [
|
|
25209
|
+
key,
|
|
25210
|
+
resolveExplainGenerateSeriesDefaults(value, literalDefaults)
|
|
25211
|
+
]));
|
|
25212
|
+
}
|
|
25213
|
+
function inferStaticExplainSchema(node, relations) {
|
|
25214
|
+
if (node.type === "SHOW_APPS") return { status: "STATIC", columns: [...SHOW_APPS_COLUMNS] };
|
|
25215
|
+
if (node.type === "DESCRIBE") return { status: "STATIC", columns: [...DESCRIBE_COLUMNS] };
|
|
25216
|
+
if (node.type === "GENERATE_SERIES") {
|
|
25217
|
+
return { status: "STATIC", columns: [node.columnAlias] };
|
|
25218
|
+
}
|
|
25219
|
+
if (node.type === "WITH") {
|
|
25220
|
+
const local = new Map(relations);
|
|
25221
|
+
for (const cte of node.ctes) {
|
|
25222
|
+
const schema = inferStaticExplainSchema(cte.query, local);
|
|
25223
|
+
local.set(cte.name, schema);
|
|
25224
|
+
}
|
|
25225
|
+
return inferStaticExplainSchema(node.query, local);
|
|
25226
|
+
}
|
|
25227
|
+
if (node.type === "UNION") {
|
|
25228
|
+
const left = inferStaticExplainSchema(node.left, relations);
|
|
25229
|
+
const right = inferStaticExplainSchema(node.right, relations);
|
|
25230
|
+
return left.status === "STATIC" && right.status === "STATIC" ? { status: "STATIC", columns: left.columns } : { status: "DEFERRED" };
|
|
25231
|
+
}
|
|
25232
|
+
const sources = [node.from, ...node.joins.map((join2) => join2.table)];
|
|
25233
|
+
const relationSources = sources.filter((source) => source.cteName !== null && source.cteName !== NO_FROM_CTE_NAME);
|
|
25234
|
+
if (relationSources.some((source) => relations.get(source.cteName)?.status !== "STATIC")) {
|
|
25235
|
+
return { status: "DEFERRED" };
|
|
25236
|
+
}
|
|
25237
|
+
const hasWildcard = node.columns.some(
|
|
25238
|
+
(column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD"
|
|
25239
|
+
);
|
|
25240
|
+
if (hasWildcard) {
|
|
25241
|
+
if (sources.length !== 1 || sources[0].cteName === null) return { status: "DEFERRED" };
|
|
25242
|
+
}
|
|
25243
|
+
const sourceColumns2 = relationSources.flatMap((source) => {
|
|
25244
|
+
const schema = relations.get(source.cteName);
|
|
25245
|
+
return schema?.status === "STATIC" ? [...schema.columns] : [];
|
|
25246
|
+
});
|
|
25247
|
+
const columns = [];
|
|
25248
|
+
for (const column of node.columns) {
|
|
25249
|
+
if (column.type === "WILDCARD") columns.push(...sourceColumns2);
|
|
25250
|
+
else if (column.type === "PARENT_WILDCARD") {
|
|
25251
|
+
columns.push(...sourceColumns2.filter((name) => name.startsWith("_p.")));
|
|
25252
|
+
} else {
|
|
25253
|
+
columns.push(...project([], [column]).columns);
|
|
25254
|
+
}
|
|
25255
|
+
}
|
|
25256
|
+
return { status: "STATIC", columns };
|
|
25257
|
+
}
|
|
25258
|
+
function staticSchemaRelations(ledger) {
|
|
25259
|
+
return new Map([...ledger].flatMap(
|
|
25260
|
+
([name, entry]) => entry.status === "STATIC" ? [[name, entry.relation]] : []
|
|
25261
|
+
));
|
|
25262
|
+
}
|
|
25263
|
+
async function buildStaticTempPlainGroupByPlans(node, client, cacheContext, relations) {
|
|
25264
|
+
const plans = /* @__PURE__ */ new Map();
|
|
25265
|
+
const visit = async (value) => {
|
|
25266
|
+
if (value === null || typeof value !== "object") return;
|
|
25267
|
+
if (Array.isArray(value)) {
|
|
25268
|
+
for (const child of value) await visit(child);
|
|
25269
|
+
return;
|
|
25270
|
+
}
|
|
25271
|
+
const object = value;
|
|
25272
|
+
if (object["type"] === "SELECT") {
|
|
25273
|
+
const select = value;
|
|
25274
|
+
const sources = [select.from, ...select.joins.map((join2) => join2.table)];
|
|
25275
|
+
if (sources.some((source) => source.cteName !== null) && sources.every((source) => source.cteName === NO_FROM_CTE_NAME || source.cteName !== null && relations.has(source.cteName))) {
|
|
25276
|
+
const plan = await buildRuntimePlainGroupByPlan(select, client, cacheContext, relations);
|
|
25277
|
+
if (plan) plans.set(select, plan);
|
|
25278
|
+
}
|
|
25279
|
+
}
|
|
25280
|
+
for (const child of Object.values(object)) await visit(child);
|
|
25281
|
+
};
|
|
25282
|
+
await visit(node);
|
|
25283
|
+
return plans;
|
|
25284
|
+
}
|
|
24956
25285
|
var EXPLAIN_FETCH_PLAN = /* @__PURE__ */ Symbol("ksql.explainFetchPlan");
|
|
24957
25286
|
function setExplainFetchPlan(result, plan) {
|
|
24958
25287
|
result[EXPLAIN_FETCH_PLAN] = plan;
|
|
@@ -24965,19 +25294,27 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
24965
25294
|
const normalizedInjectedVariables = validateDeclaredBatchVariables(statements, injectedVariables);
|
|
24966
25295
|
const relativeDateVariables = prepareRelativeDateVariables(statements, normalizedInjectedVariables);
|
|
24967
25296
|
const variables = /* @__PURE__ */ new Map();
|
|
25297
|
+
const literalDeclareDefaults = /* @__PURE__ */ new Map();
|
|
25298
|
+
const tempSchemaLedger = /* @__PURE__ */ new Map();
|
|
24968
25299
|
const plans = [];
|
|
24969
25300
|
const fetchStatements = [];
|
|
24970
25301
|
for (let i = 0; i < statements.length; i++) {
|
|
24971
25302
|
const stmt = statements[i];
|
|
24972
|
-
const
|
|
25303
|
+
const placeholderResolvedStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveBatchVariableReferences(stmt.expr, variables) } : stmt : resolveBatchVariableReferences(stmt, variables);
|
|
25304
|
+
const planStmt = resolveExplainGenerateSeriesDefaults(
|
|
25305
|
+
placeholderResolvedStmt,
|
|
25306
|
+
literalDeclareDefaults
|
|
25307
|
+
);
|
|
24973
25308
|
validateStatementStatic(planStmt);
|
|
24974
25309
|
const relativeDatePlan = await resolveRelativeDateExecutionPlan(planStmt, client, invocationCacheContext);
|
|
25310
|
+
const initialRelations = staticSchemaRelations(tempSchemaLedger);
|
|
24975
25311
|
const whereAnalysis = resolveMetadata ? await buildExplainWhereAnalysis(
|
|
24976
25312
|
planStmt,
|
|
24977
25313
|
client,
|
|
24978
25314
|
invocationCacheContext,
|
|
24979
25315
|
maxRecords,
|
|
24980
|
-
relativeDatePlan
|
|
25316
|
+
relativeDatePlan,
|
|
25317
|
+
initialRelations
|
|
24981
25318
|
) : {
|
|
24982
25319
|
capabilities: /* @__PURE__ */ new Map(),
|
|
24983
25320
|
orderPlans: /* @__PURE__ */ new Map(),
|
|
@@ -24987,6 +25324,34 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
24987
25324
|
numberPrecisionApps: /* @__PURE__ */ new Set(),
|
|
24988
25325
|
relativeDatePlan
|
|
24989
25326
|
};
|
|
25327
|
+
if (!resolveMetadata) {
|
|
25328
|
+
const staticPlans = await buildStaticTempPlainGroupByPlans(
|
|
25329
|
+
planStmt,
|
|
25330
|
+
client,
|
|
25331
|
+
invocationCacheContext,
|
|
25332
|
+
initialRelations
|
|
25333
|
+
);
|
|
25334
|
+
for (const [select, plan] of staticPlans) whereAnalysis.plainGroupByPlans.set(select, plan);
|
|
25335
|
+
}
|
|
25336
|
+
let createdSchema;
|
|
25337
|
+
if (planStmt.type === "CREATE_TEMP_TABLE") {
|
|
25338
|
+
const relationSchemas = new Map([...tempSchemaLedger].map(([name, entry]) => [
|
|
25339
|
+
name,
|
|
25340
|
+
entry.status === "STATIC" ? { status: "STATIC", columns: entry.columns } : { status: "DEFERRED" }
|
|
25341
|
+
]));
|
|
25342
|
+
const inferred = inferStaticExplainSchema(planStmt.query, relationSchemas);
|
|
25343
|
+
createdSchema = inferred.status === "STATIC" ? {
|
|
25344
|
+
status: "STATIC",
|
|
25345
|
+
columns: inferred.columns,
|
|
25346
|
+
relation: { rows: [], columns: [...inferred.columns] },
|
|
25347
|
+
producerStatement: i + 1
|
|
25348
|
+
} : {
|
|
25349
|
+
status: "DEFERRED",
|
|
25350
|
+
producerStatement: i + 1,
|
|
25351
|
+
reason: "EXPLAIN_TEMP_SCHEMA_UNAVAILABLE"
|
|
25352
|
+
};
|
|
25353
|
+
tempSchemaLedger.set(planStmt.name, createdSchema);
|
|
25354
|
+
}
|
|
24990
25355
|
const fetchCollector = { sources: [] };
|
|
24991
25356
|
const statementPlan = relativeDatePlan.hasServerOnlyWhereFunction && !relativeDatePlan.allowed ? relativeDateExplainLines(relativeDatePlan) : [
|
|
24992
25357
|
...relativeDateExplainLines(relativeDatePlan),
|
|
@@ -24995,9 +25360,12 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
24995
25360
|
analysis.statements[i],
|
|
24996
25361
|
whereAnalysis.capabilities,
|
|
24997
25362
|
whereAnalysis.orderPlans,
|
|
25363
|
+
whereAnalysis.plainGroupByPlans,
|
|
24998
25364
|
dmlMaxRows,
|
|
24999
25365
|
dmlMaxSubtableRows,
|
|
25000
|
-
fetchCollector
|
|
25366
|
+
fetchCollector,
|
|
25367
|
+
tempSchemaLedger,
|
|
25368
|
+
createdSchema
|
|
25001
25369
|
), cursorMaxActive)
|
|
25002
25370
|
];
|
|
25003
25371
|
const metadataPlan = explainMetadataLines(whereAnalysis);
|
|
@@ -25011,8 +25379,15 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
25011
25379
|
fetch: worstExplainFetch(fetchCollector.sources),
|
|
25012
25380
|
sources: fetchCollector.sources
|
|
25013
25381
|
});
|
|
25382
|
+
if (planStmt.type === "DROP_TEMP_TABLE") tempSchemaLedger.delete(planStmt.name);
|
|
25014
25383
|
if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
|
|
25015
25384
|
variables.set(stmt.name, stmt.type === "DECLARE_VARIABLE" && stmt.annotation === "RELATIVE_DATE" ? { type: "relative-date", value: relativeDateVariables.get(stmt.name) } : stmt.type === "SET_VARIABLE" && stmt.expr.type === "ARRAY" ? { type: "array", elements: stmt.expr.elements.map((element) => ({ type: "string", value: element.value })) } : { type: "string", value: `@${stmt.name}`, placeholder: true });
|
|
25385
|
+
if (stmt.type === "DECLARE_VARIABLE" && stmt.annotation === void 0 && (stmt.default.type === "STRING" || stmt.default.type === "NUMBER")) {
|
|
25386
|
+
literalDeclareDefaults.set(
|
|
25387
|
+
stmt.name,
|
|
25388
|
+
stmt.default.type === "STRING" ? stmt.default.value : numberLiteralText(stmt.default)
|
|
25389
|
+
);
|
|
25390
|
+
}
|
|
25016
25391
|
}
|
|
25017
25392
|
}
|
|
25018
25393
|
const result = { statementCount: statements.length, statements: plans };
|
|
@@ -25022,19 +25397,28 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
25022
25397
|
releaseMetadataCacheScope(invocationCacheContext);
|
|
25023
25398
|
}
|
|
25024
25399
|
}
|
|
25025
|
-
function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }) {
|
|
25400
|
+
function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, plainGroupByPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, collector = { sources: [] }, tempSchemaLedger = /* @__PURE__ */ new Map(), createdSchema) {
|
|
25026
25401
|
if (stmt.type === "CREATE_TEMP_TABLE") {
|
|
25027
25402
|
return [
|
|
25028
25403
|
`CREATE TEMP TABLE ${stmt.name}`,
|
|
25029
25404
|
` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
|
|
25405
|
+
...createdSchema?.status === "STATIC" ? [
|
|
25406
|
+
` schema: ${createdSchema.columns.join(", ")}`,
|
|
25407
|
+
` schema source: SELECT output of statement ${createdSchema.producerStatement}`
|
|
25408
|
+
] : createdSchema ? [
|
|
25409
|
+
" schema: deferred (could not be derived statically)",
|
|
25410
|
+
` plan status: deferred (temp table schema; reason=${createdSchema.reason})`
|
|
25411
|
+
] : [],
|
|
25030
25412
|
` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u65E2\u5B9A\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001tempTableMaxRows \u3067\u5909\u66F4\u53EF\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
|
|
25031
25413
|
...buildPlanForBatchQuery(
|
|
25032
25414
|
stmt.query,
|
|
25033
25415
|
info,
|
|
25034
25416
|
capabilities,
|
|
25035
25417
|
orderPlans,
|
|
25418
|
+
plainGroupByPlans,
|
|
25036
25419
|
collector,
|
|
25037
|
-
"main"
|
|
25420
|
+
"main",
|
|
25421
|
+
tempSchemaLedger
|
|
25038
25422
|
).map((l) => ` ${l}`)
|
|
25039
25423
|
];
|
|
25040
25424
|
}
|
|
@@ -25056,7 +25440,10 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
|
|
|
25056
25440
|
subInfo,
|
|
25057
25441
|
capabilities,
|
|
25058
25442
|
orderPlans,
|
|
25059
|
-
|
|
25443
|
+
plainGroupByPlans,
|
|
25444
|
+
collector,
|
|
25445
|
+
"main",
|
|
25446
|
+
tempSchemaLedger
|
|
25060
25447
|
).map((l) => ` ${l}`)
|
|
25061
25448
|
];
|
|
25062
25449
|
}
|
|
@@ -25085,7 +25472,10 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
|
|
|
25085
25472
|
info,
|
|
25086
25473
|
capabilities,
|
|
25087
25474
|
orderPlans,
|
|
25088
|
-
|
|
25475
|
+
plainGroupByPlans,
|
|
25476
|
+
collector,
|
|
25477
|
+
"main",
|
|
25478
|
+
tempSchemaLedger
|
|
25089
25479
|
);
|
|
25090
25480
|
}
|
|
25091
25481
|
if (stmt.type === "ASSERT") {
|
|
@@ -25104,15 +25494,36 @@ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans, dmlMaxRow
|
|
|
25104
25494
|
subInfo,
|
|
25105
25495
|
capabilities,
|
|
25106
25496
|
orderPlans,
|
|
25107
|
-
|
|
25497
|
+
plainGroupByPlans,
|
|
25498
|
+
collector,
|
|
25499
|
+
"main",
|
|
25500
|
+
tempSchemaLedger
|
|
25108
25501
|
).map((l) => ` ${l}`));
|
|
25109
25502
|
});
|
|
25110
25503
|
return lines;
|
|
25111
25504
|
}
|
|
25112
25505
|
if (stmt.type === "UPDATE" && (stmt.applyBlocks?.length ?? 0) > 0) {
|
|
25113
|
-
return buildExplainPlan(
|
|
25506
|
+
return buildExplainPlan(
|
|
25507
|
+
stmt,
|
|
25508
|
+
void 0,
|
|
25509
|
+
capabilities,
|
|
25510
|
+
orderPlans,
|
|
25511
|
+
dmlMaxRows,
|
|
25512
|
+
dmlMaxSubtableRows,
|
|
25513
|
+
1e4,
|
|
25514
|
+
plainGroupByPlans
|
|
25515
|
+
);
|
|
25114
25516
|
}
|
|
25115
|
-
return buildPlanForBatchQuery(
|
|
25517
|
+
return buildPlanForBatchQuery(
|
|
25518
|
+
stmt,
|
|
25519
|
+
info,
|
|
25520
|
+
capabilities,
|
|
25521
|
+
orderPlans,
|
|
25522
|
+
plainGroupByPlans,
|
|
25523
|
+
collector,
|
|
25524
|
+
"main",
|
|
25525
|
+
tempSchemaLedger
|
|
25526
|
+
);
|
|
25116
25527
|
}
|
|
25117
25528
|
function hasTempTableRef(node) {
|
|
25118
25529
|
if (Array.isArray(node)) return node.some(hasTempTableRef);
|
|
@@ -25124,7 +25535,7 @@ function hasTempTableRef(node) {
|
|
|
25124
25535
|
}
|
|
25125
25536
|
return false;
|
|
25126
25537
|
}
|
|
25127
|
-
function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector = { sources: [] }, sourceRole = "main") {
|
|
25538
|
+
function buildPlanForBatchQuery(query, info, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }, sourceRole = "main", tempSchemaLedger = /* @__PURE__ */ new Map()) {
|
|
25128
25539
|
if (info.tempTablesReferenced.length === 0) {
|
|
25129
25540
|
return buildExplainPlan(
|
|
25130
25541
|
query,
|
|
@@ -25134,7 +25545,7 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector
|
|
|
25134
25545
|
100,
|
|
25135
25546
|
DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
|
|
25136
25547
|
1e4,
|
|
25137
|
-
|
|
25548
|
+
plainGroupByPlans,
|
|
25138
25549
|
true,
|
|
25139
25550
|
collector,
|
|
25140
25551
|
sourceRole
|
|
@@ -25154,6 +25565,26 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector
|
|
|
25154
25565
|
lines.push(
|
|
25155
25566
|
` temp: ${info.tempTablesReferenced.join(", ")}\uFF08\u30A4\u30F3\u30E1\u30E2\u30EA\u8D70\u67FB\u3002\u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u884C\u6570\u4E0D\u660E\uFF09`
|
|
25156
25567
|
);
|
|
25568
|
+
const entries = info.tempTablesReferenced.map((name) => [name, tempSchemaLedger.get(name)]);
|
|
25569
|
+
for (const [name, entry] of entries) {
|
|
25570
|
+
if (entry?.status === "STATIC") {
|
|
25571
|
+
lines.push(` source: temp table ${name} (schema from statement ${entry.producerStatement})`);
|
|
25572
|
+
} else {
|
|
25573
|
+
lines.push(` source: temp table ${name}`);
|
|
25574
|
+
lines.push(" schema: deferred (could not be derived statically)");
|
|
25575
|
+
}
|
|
25576
|
+
}
|
|
25577
|
+
lines.push(" rows: runtime (not materialized by EXPLAIN)");
|
|
25578
|
+
const directSelect = query.type === "SELECT" ? query : query.type === "EXPLAIN" && query.query.type === "SELECT" ? query.query : void 0;
|
|
25579
|
+
if (directSelect) {
|
|
25580
|
+
lines.push(...renderPlainGroupByExplainLines(
|
|
25581
|
+
directSelect,
|
|
25582
|
+
plainGroupByPlans?.get(directSelect),
|
|
25583
|
+
entries.some(([, entry]) => entry?.status !== "STATIC")
|
|
25584
|
+
));
|
|
25585
|
+
}
|
|
25586
|
+
lines.push(entries.every(([, entry]) => entry?.status === "STATIC") ? " plan status: static schema / runtime rows" : " plan status: deferred (temp table schema)");
|
|
25587
|
+
lines.push(" records API: none");
|
|
25157
25588
|
const apps = info.appIds.filter(
|
|
25158
25589
|
(a) => query.type !== "INSERT_SELECT" && query.type !== "UPSERT_SELECT" || a !== query.appId
|
|
25159
25590
|
);
|
|
@@ -25423,6 +25854,33 @@ function formatChoiceEqualityRewrite(rewrite) {
|
|
|
25423
25854
|
const normalizedOperator = rewrite.normalizedOperator === "IN" ? "in" : "not in";
|
|
25424
25855
|
return ` pushdown normalized: ${field} ${rewrite.originalOperator} '${originalValue}' -> ${field} ${normalizedOperator} ("${normalizedValue}")`;
|
|
25425
25856
|
}
|
|
25857
|
+
function renderPlainGroupByExplainLines(stmt, plainGroupByPlan, schemaDeferred) {
|
|
25858
|
+
const normalizedGrouping = normalizeGroupingSpec(stmt);
|
|
25859
|
+
if (normalizedGrouping.type !== "PLAIN") return [];
|
|
25860
|
+
const lines = [];
|
|
25861
|
+
if (plainGroupByPlan) {
|
|
25862
|
+
plainGroupByPlan.items.forEach((item, index) => {
|
|
25863
|
+
const key = normalizedGrouping.allItems[index];
|
|
25864
|
+
if (key?.type !== "FIELD_NAME") return;
|
|
25865
|
+
if (item.kind === "PHYSICAL") {
|
|
25866
|
+
lines.push(
|
|
25867
|
+
` group key ${key.name}: PHYSICAL (source=${item.sourceIndex}, field=${item.fieldCode})`
|
|
25868
|
+
);
|
|
25869
|
+
} else if (item.kind === "ALIAS_SAFE") {
|
|
25870
|
+
lines.push(` group key ${key.name}: ALIAS_SAFE (column=${item.columnIndex})`);
|
|
25871
|
+
} else if (item.kind === "EXPRESSION") {
|
|
25872
|
+
lines.push(` group key ${key.name}: EXPRESSION`);
|
|
25873
|
+
}
|
|
25874
|
+
});
|
|
25875
|
+
} else if (schemaDeferred) {
|
|
25876
|
+
for (const key of normalizedGrouping.allItems) {
|
|
25877
|
+
if (key.type === "FIELD_NAME") {
|
|
25878
|
+
lines.push(` group key ${key.name}: DEFERRED (temp table schema unavailable)`);
|
|
25879
|
+
}
|
|
25880
|
+
}
|
|
25881
|
+
}
|
|
25882
|
+
return lines;
|
|
25883
|
+
}
|
|
25426
25884
|
function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans, allowTotalCountPlan = true, emitFetch = true, collector = { sources: [] }, sourceRole = "main") {
|
|
25427
25885
|
const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
25428
25886
|
const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
@@ -25440,6 +25898,23 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25440
25898
|
);
|
|
25441
25899
|
if (label) lines.push(label);
|
|
25442
25900
|
lines.push(` mode: ${mode}`);
|
|
25901
|
+
for (const step of explainCrossJoinSteps.get(stmt) ?? []) {
|
|
25902
|
+
lines.push(` cross join: ${step.leftLabel} \xD7 ${step.rightLabel}`);
|
|
25903
|
+
lines.push(` left rows: ${step.leftRows ?? "runtime (left intermediate rows)"}`);
|
|
25904
|
+
lines.push(` right rows: ${step.rightRows ?? `runtime (${step.rightRuntimeLabel})`}`);
|
|
25905
|
+
if (step.plan) {
|
|
25906
|
+
lines.push(` rows: ${step.plan.outputRows}`);
|
|
25907
|
+
lines.push(` row guard: ${step.plan.outputRows} / ${step.plan.limit}`);
|
|
25908
|
+
lines.push(" guard timing: before row materialization");
|
|
25909
|
+
} else {
|
|
25910
|
+
const left = step.leftRows === null ? "left rows" : String(step.leftRows);
|
|
25911
|
+
const right = step.rightRows === null ? "right rows" : String(step.rightRows);
|
|
25912
|
+
lines.push(` rows: runtime (${left} \xD7 ${right})`);
|
|
25913
|
+
lines.push(` row guard: runtime checked / ${CROSS_JOIN_MAX_ROWS}`);
|
|
25914
|
+
lines.push(" guard timing: after complete source fetch, before row materialization");
|
|
25915
|
+
}
|
|
25916
|
+
lines.push(" records API: none");
|
|
25917
|
+
}
|
|
25443
25918
|
for (const rewrite of explainChoiceEqualityRewrites.get(stmt) ?? choiceEqualityRewritesBySelect.get(stmt) ?? []) {
|
|
25444
25919
|
lines.push(formatChoiceEqualityRewrite(rewrite));
|
|
25445
25920
|
}
|
|
@@ -25455,35 +25930,11 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25455
25930
|
` grouping output rows: runtime checked (limit: ${groupingMetadata.outputRowLimit}, before HAVING/DISTINCT/LIMIT)`
|
|
25456
25931
|
);
|
|
25457
25932
|
}
|
|
25458
|
-
|
|
25459
|
-
|
|
25460
|
-
|
|
25461
|
-
|
|
25462
|
-
|
|
25463
|
-
const key = groupBy[index];
|
|
25464
|
-
if (key?.type !== "FIELD_NAME") return;
|
|
25465
|
-
if (item.kind === "PHYSICAL") {
|
|
25466
|
-
lines.push(
|
|
25467
|
-
` group key ${key.name}: PHYSICAL (source=${item.sourceIndex}, field=${item.fieldCode})`
|
|
25468
|
-
);
|
|
25469
|
-
} else if (item.kind === "ALIAS_SAFE") {
|
|
25470
|
-
lines.push(
|
|
25471
|
-
` group key ${key.name}: ALIAS_SAFE (column=${item.columnIndex})`
|
|
25472
|
-
);
|
|
25473
|
-
} else if (item.kind === "EXPRESSION") {
|
|
25474
|
-
lines.push(` group key ${key.name}: EXPRESSION`);
|
|
25475
|
-
}
|
|
25476
|
-
});
|
|
25477
|
-
} else if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) {
|
|
25478
|
-
for (const key of groupBy) {
|
|
25479
|
-
if (key.type === "FIELD_NAME") {
|
|
25480
|
-
lines.push(
|
|
25481
|
-
` group key ${key.name}: DEFERRED (materialized schema unavailable)`
|
|
25482
|
-
);
|
|
25483
|
-
}
|
|
25484
|
-
}
|
|
25485
|
-
}
|
|
25486
|
-
}
|
|
25933
|
+
lines.push(...renderPlainGroupByExplainLines(
|
|
25934
|
+
stmt,
|
|
25935
|
+
plainGroupByPlan,
|
|
25936
|
+
[stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)
|
|
25937
|
+
));
|
|
25487
25938
|
for (const column of stmt.columns) {
|
|
25488
25939
|
if (column.type !== "WINDOW_COL" || column.windowKind === void 0 || column.windowKind === "RANKING") continue;
|
|
25489
25940
|
const clauses = [];
|
|
@@ -25604,7 +26055,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
25604
26055
|
}
|
|
25605
26056
|
}
|
|
25606
26057
|
} else {
|
|
25607
|
-
const reason = stmt.joins.some((join2) => join2.type !== "INNER") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
|
|
26058
|
+
const reason = stmt.joins.some((join2) => join2.type !== "INNER" && join2.type !== "CROSS") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
|
|
25608
26059
|
(table) => table.cteName !== null || Boolean(table.subtableCode)
|
|
25609
26060
|
) ? "SOURCE_KIND" : "PLAN_NOT_APPLICABLE";
|
|
25610
26061
|
lines.push(" join pushdown plan: not applied (join key/WHERE prefilters are reported per source below)");
|
|
@@ -25756,7 +26207,69 @@ function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans, colle
|
|
|
25756
26207
|
});
|
|
25757
26208
|
return lines;
|
|
25758
26209
|
}
|
|
26210
|
+
function populateWithCrossJoinExplain(stmt) {
|
|
26211
|
+
const exactRows = /* @__PURE__ */ new Map();
|
|
26212
|
+
const labelFor = (table) => effectiveTableAlias(table) ?? (table.appId > 0 ? `APP${table.appId}` : "source");
|
|
26213
|
+
const rowsFor = (table) => {
|
|
26214
|
+
if (table.cteName === NO_FROM_CTE_NAME) return 1;
|
|
26215
|
+
return table.cteName === null ? null : exactRows.get(table.cteName) ?? null;
|
|
26216
|
+
};
|
|
26217
|
+
const analyze = (select) => {
|
|
26218
|
+
let current = rowsFor(select.from);
|
|
26219
|
+
let leftLabel = labelFor(select.from);
|
|
26220
|
+
const steps = [];
|
|
26221
|
+
for (const join2 of select.joins) {
|
|
26222
|
+
const right = rowsFor(join2.table);
|
|
26223
|
+
const rightLabel = labelFor(join2.table);
|
|
26224
|
+
if (join2.type === "CROSS") {
|
|
26225
|
+
const plan = current !== null && right !== null ? planCrossJoinRows(current, right) : null;
|
|
26226
|
+
steps.push({
|
|
26227
|
+
leftLabel,
|
|
26228
|
+
rightLabel,
|
|
26229
|
+
leftRows: current,
|
|
26230
|
+
rightRows: right,
|
|
26231
|
+
plan,
|
|
26232
|
+
rightRuntimeLabel: join2.table.cteName === null ? `APP${join2.table.appId} fetched rows` : `${rightLabel} materialized rows`
|
|
26233
|
+
});
|
|
26234
|
+
current = plan?.outputRows ?? null;
|
|
26235
|
+
leftLabel = `${leftLabel} \xD7 ${rightLabel}`;
|
|
26236
|
+
} else {
|
|
26237
|
+
current = null;
|
|
26238
|
+
leftLabel = `${leftLabel} ${join2.type} JOIN ${rightLabel}`;
|
|
26239
|
+
}
|
|
26240
|
+
}
|
|
26241
|
+
if (steps.length > 0) explainCrossJoinSteps.set(select, steps);
|
|
26242
|
+
if (isConstantFalseWhere(select.where)) current = 0;
|
|
26243
|
+
else if (select.where !== null) current = null;
|
|
26244
|
+
if (normalizeGroupingSpec(select).type !== "NONE" || select.distinct || select.having !== null || isAggregateQueryBlock(select) || select.columns.some((column) => column.type === "WINDOW_COL")) return null;
|
|
26245
|
+
if (current === null) return null;
|
|
26246
|
+
current = Math.max(0, current - (select.offset ?? 0));
|
|
26247
|
+
return select.limit === null ? current : Math.min(current, select.limit);
|
|
26248
|
+
};
|
|
26249
|
+
const analyzeQuery = (query) => {
|
|
26250
|
+
if (query.type === "SELECT") {
|
|
26251
|
+
analyze(query);
|
|
26252
|
+
return;
|
|
26253
|
+
}
|
|
26254
|
+
analyzeQuery(query.left);
|
|
26255
|
+
analyze(query.right);
|
|
26256
|
+
};
|
|
26257
|
+
for (const cte of stmt.ctes) {
|
|
26258
|
+
if (cte.query.type === "GENERATE_SERIES") {
|
|
26259
|
+
if (!cte.query.args.some((arg) => arg.type === "VARIABLE")) {
|
|
26260
|
+
exactRows.set(cte.name, resolveGenerateSeries(cte.query).rowCount);
|
|
26261
|
+
}
|
|
26262
|
+
} else if (cte.query.type === "SELECT") {
|
|
26263
|
+
const rows = analyze(cte.query);
|
|
26264
|
+
if (rows !== null) exactRows.set(cte.name, rows);
|
|
26265
|
+
} else if (cte.query.type === "UNION") {
|
|
26266
|
+
analyzeQuery(cte.query);
|
|
26267
|
+
}
|
|
26268
|
+
}
|
|
26269
|
+
analyzeQuery(stmt.query);
|
|
26270
|
+
}
|
|
25759
26271
|
function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collector = { sources: [] }) {
|
|
26272
|
+
populateWithCrossJoinExplain(stmt);
|
|
25760
26273
|
const lines = [];
|
|
25761
26274
|
for (const cte of stmt.ctes) {
|
|
25762
26275
|
if (cte.query.type === "SELECT") {
|
|
@@ -25773,18 +26286,45 @@ function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans, collec
|
|
|
25773
26286
|
));
|
|
25774
26287
|
lines.push("");
|
|
25775
26288
|
} else if (cte.query.type === "GENERATE_SERIES") {
|
|
25776
|
-
const
|
|
25777
|
-
const
|
|
26289
|
+
const seriesStatement = cte.query;
|
|
26290
|
+
const binding = explainSeriesBindings.get(seriesStatement);
|
|
26291
|
+
const unresolved = seriesStatement.args.some((arg) => arg.type === "VARIABLE");
|
|
26292
|
+
if (unresolved) {
|
|
26293
|
+
const argumentLabel = (index) => {
|
|
26294
|
+
const arg = seriesStatement.args[index];
|
|
26295
|
+
if (!arg) return index === 2 ? "runtime" : "deferred";
|
|
26296
|
+
if (arg.type === "VARIABLE") return `@${arg.name} (runtime)`;
|
|
26297
|
+
if (index < 2) return "literal";
|
|
26298
|
+
return arg.type === "NUMBER" ? numberLiteralText(arg) : arg.value;
|
|
26299
|
+
};
|
|
26300
|
+
lines.push(
|
|
26301
|
+
`[cte: ${cte.name}]`,
|
|
26302
|
+
" source: GENERATE_SERIES",
|
|
26303
|
+
` column: ${seriesStatement.columnAlias}`,
|
|
26304
|
+
" series type: deferred (variable)",
|
|
26305
|
+
` start: ${argumentLabel(0)}`,
|
|
26306
|
+
` stop: ${argumentLabel(1)}`,
|
|
26307
|
+
` step: ${argumentLabel(2)}`,
|
|
26308
|
+
" rows: runtime",
|
|
26309
|
+
` row guard: runtime / ${GENERATE_SERIES_MAX_ROWS}`,
|
|
26310
|
+
" records API: none",
|
|
26311
|
+
""
|
|
26312
|
+
);
|
|
26313
|
+
continue;
|
|
26314
|
+
}
|
|
26315
|
+
const series = resolveGenerateSeries(seriesStatement);
|
|
26316
|
+
const step = series.kind === "DATE" ? `${series.step} ${String(series.dateUnit ?? "DAY").toLowerCase()}${Math.abs(series.step) === 1 ? "" : "s"}` : String(series.step);
|
|
25778
26317
|
lines.push(
|
|
25779
26318
|
`[cte: ${cte.name}]`,
|
|
25780
26319
|
" source: GENERATE_SERIES",
|
|
25781
|
-
` column: ${
|
|
25782
|
-
` series type: ${series.kind}`,
|
|
25783
|
-
` start: ${series.start}`,
|
|
25784
|
-
` stop: ${series.stop}`,
|
|
26320
|
+
` column: ${seriesStatement.columnAlias}`,
|
|
26321
|
+
` series type: ${series.kind}${binding?.defaultBoundIndexes.size ? " (DECLARE default)" : ""}`,
|
|
26322
|
+
` start: ${binding?.variableNames.has(0) ? `@${binding.variableNames.get(0)} (DECLARE default; value hidden)` : series.start}`,
|
|
26323
|
+
` stop: ${binding?.variableNames.has(1) ? `@${binding.variableNames.get(1)} (DECLARE default; value hidden)` : series.stop}`,
|
|
25785
26324
|
` step: ${step}`,
|
|
25786
|
-
` rows: ${series.rowCount}`,
|
|
26325
|
+
` rows: ${series.rowCount}${binding?.defaultBoundIndexes.size ? " (DECLARE default estimate)" : ""}`,
|
|
25787
26326
|
` row guard: ${series.rowCount} / ${GENERATE_SERIES_MAX_ROWS}`,
|
|
26327
|
+
...binding?.defaultBoundIndexes.size ? [" binding: DECLARE defaults; runtime injection may change this plan"] : [],
|
|
25788
26328
|
" records API: none",
|
|
25789
26329
|
""
|
|
25790
26330
|
);
|
|
@@ -25825,7 +26365,9 @@ function collectFullScanReasons(stmt) {
|
|
|
25825
26365
|
const r = [];
|
|
25826
26366
|
if (stmt.from.subtableCode || stmt.joins.some((j) => j.table.subtableCode))
|
|
25827
26367
|
r.push("\u30B5\u30D6\u30C6\u30FC\u30D6\u30EB\u4EEE\u60F3\u30C6\u30FC\u30D6\u30EB");
|
|
25828
|
-
if (stmt.joins.
|
|
26368
|
+
if (stmt.joins.some((join2) => join2.type === "CROSS"))
|
|
26369
|
+
r.push("CROSS JOIN \u3042\u308A");
|
|
26370
|
+
if (stmt.joins.some((join2) => join2.type !== "CROSS"))
|
|
25829
26371
|
r.push("JOIN \u3042\u308A");
|
|
25830
26372
|
const grouping = normalizeGroupingSpec(stmt);
|
|
25831
26373
|
if (grouping.type === "PLAIN")
|
|
@@ -28906,14 +29448,42 @@ function createDryRunClient() {
|
|
|
28906
29448
|
function hasStaticTypedPushdownCandidate(statement) {
|
|
28907
29449
|
if (statement === null || typeof statement !== "object") return false;
|
|
28908
29450
|
const node = statement;
|
|
28909
|
-
if (node["type"] === "
|
|
29451
|
+
if (node["type"] === "CREATE_TEMP_TABLE") {
|
|
29452
|
+
return hasStaticTypedPushdownCandidate(node["query"]);
|
|
29453
|
+
}
|
|
29454
|
+
if (node["type"] === "WITH") {
|
|
29455
|
+
const query = node["query"];
|
|
29456
|
+
const containsCross = (value) => {
|
|
29457
|
+
if (Array.isArray(value)) return value.some(containsCross);
|
|
29458
|
+
if (value === null || typeof value !== "object") return false;
|
|
29459
|
+
const item = value;
|
|
29460
|
+
if (item["type"] === "SELECT") {
|
|
29461
|
+
if (Array.isArray(item["joins"]) && item["joins"].some(
|
|
29462
|
+
(join2) => typeof join2 === "object" && join2 !== null && join2["type"] === "CROSS"
|
|
29463
|
+
)) return true;
|
|
29464
|
+
}
|
|
29465
|
+
return Object.values(item).some(containsCross);
|
|
29466
|
+
};
|
|
29467
|
+
const hasPhysicalCte = Array.isArray(node["ctes"]) && node["ctes"].some((cte) => {
|
|
29468
|
+
if (typeof cte !== "object" || cte === null) return false;
|
|
29469
|
+
const cteQuery = cte["query"];
|
|
29470
|
+
if (typeof cteQuery !== "object" || cteQuery === null) return false;
|
|
29471
|
+
const from = cteQuery["from"];
|
|
29472
|
+
return cteQuery["type"] === "SELECT" && typeof from === "object" && from !== null && from["cteName"] === null;
|
|
29473
|
+
});
|
|
29474
|
+
return (containsCross(node["ctes"]) || containsCross(query)) && hasPhysicalCte || hasStaticTypedPushdownCandidate(query);
|
|
29475
|
+
}
|
|
28910
29476
|
if (node["type"] !== "SELECT") return false;
|
|
28911
29477
|
const select = statement;
|
|
28912
|
-
if (!
|
|
29478
|
+
if (!Array.isArray(select.joins) || select.joins.length === 0) return false;
|
|
28913
29479
|
if (![select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) return false;
|
|
29480
|
+
if (select.joins.some((join2) => join2.type === "CROSS" && join2.table.cteName === null)) {
|
|
29481
|
+
return true;
|
|
29482
|
+
}
|
|
29483
|
+
if (!select.where) return false;
|
|
28914
29484
|
const where = select.where;
|
|
28915
29485
|
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
|
|
29486
|
+
(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
29487
|
);
|
|
28918
29488
|
}
|
|
28919
29489
|
async function runDiagnosticRecordGet(params) {
|
|
@@ -29600,7 +30170,10 @@ async function run() {
|
|
|
29600
30170
|
return false;
|
|
29601
30171
|
});
|
|
29602
30172
|
dryRunNeedsMetadata = statements.some(explainNeedsAppMetadata);
|
|
29603
|
-
|
|
30173
|
+
const staticEligible = statements.every(
|
|
30174
|
+
(statement) => !explainNeedsAppMetadata(statement) || hasStaticTypedPushdownCandidate(statement)
|
|
30175
|
+
);
|
|
30176
|
+
dryRunUsesStaticTypedPlan = statements.some(hasStaticTypedPushdownCandidate) && staticEligible && !statements.some(statementUsesRelativeDateResolution);
|
|
29604
30177
|
if (statements.length > 1) {
|
|
29605
30178
|
batchAnalysis = analyzeBatch(statements);
|
|
29606
30179
|
isBatchSql = true;
|
|
@@ -29987,7 +30560,7 @@ async function run() {
|
|
|
29987
30560
|
Object.keys(args.importCsv).length > 0 || Object.keys(args.importJson).length > 0,
|
|
29988
30561
|
dmlMaxRows,
|
|
29989
30562
|
dmlMaxSubtableRows,
|
|
29990
|
-
|
|
30563
|
+
!dryRunUsesStaticTypedPlan
|
|
29991
30564
|
);
|
|
29992
30565
|
const out = [];
|
|
29993
30566
|
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|