@rex0220/kintone-sql-tools 3.22.0 → 3.23.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 +411 -77
- package/dist-engine/index.cjs +8 -8
- package/dist-engine/index.mjs +8 -8
- package/dist-engine/ksql-engine.umd.js +8 -8
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +54 -12
- package/dist-engine/meta/esm.json +54 -12
- package/dist-engine/meta/umd.json +54 -12
- package/dist-mcp/ksql-mcp.js +417 -79
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -5186,8 +5186,8 @@ function selectToFetchAllParams(stmt, appId) {
|
|
|
5186
5186
|
// 全件取得なので全フィールドを取得する
|
|
5187
5187
|
};
|
|
5188
5188
|
}
|
|
5189
|
-
function selectToFetchAllFields(stmt, targetTable) {
|
|
5190
|
-
const plan = collectRequiredFieldsByTable(stmt);
|
|
5189
|
+
function selectToFetchAllFields(stmt, targetTable, plainGroupByPlan) {
|
|
5190
|
+
const plan = collectRequiredFieldsByTable(stmt, plainGroupByPlan);
|
|
5191
5191
|
const target = plan.get(targetTable);
|
|
5192
5192
|
if (!target) return [];
|
|
5193
5193
|
if (target.allFields) return [];
|
|
@@ -5312,7 +5312,8 @@ function caseResultHasAggregate(result) {
|
|
|
5312
5312
|
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
5313
5313
|
return scalarValueHasAggregate(result);
|
|
5314
5314
|
}
|
|
5315
|
-
function collectRequiredFieldsByTable(stmt) {
|
|
5315
|
+
function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
|
|
5316
|
+
const allTables = [stmt.from, ...stmt.joins.map((j) => j.table)];
|
|
5316
5317
|
const physicalTables = [stmt.from, ...stmt.joins.map((j) => j.table)].filter((t) => t.cteName === null);
|
|
5317
5318
|
const states = /* @__PURE__ */ new Map();
|
|
5318
5319
|
for (const table of physicalTables) {
|
|
@@ -5361,7 +5362,7 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
5361
5362
|
if (!fieldName) return;
|
|
5362
5363
|
st.fields.add(fieldName);
|
|
5363
5364
|
};
|
|
5364
|
-
const addFieldName = (rawName, phase = "select") => {
|
|
5365
|
+
const addFieldName = (rawName, phase = "select", groupResolution) => {
|
|
5365
5366
|
if (!rawName || rawName === "*") return;
|
|
5366
5367
|
if (rawName === "_p.*") {
|
|
5367
5368
|
markAllSubtableTables();
|
|
@@ -5384,6 +5385,15 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
5384
5385
|
}
|
|
5385
5386
|
return;
|
|
5386
5387
|
}
|
|
5388
|
+
if (phase === "groupBy" && groupResolution !== void 0) {
|
|
5389
|
+
if (groupResolution.kind === "PHYSICAL") {
|
|
5390
|
+
const source = allTables[groupResolution.sourceIndex];
|
|
5391
|
+
if (source?.cteName === null) {
|
|
5392
|
+
addFieldToTable(source, groupResolution.fieldCode);
|
|
5393
|
+
}
|
|
5394
|
+
}
|
|
5395
|
+
return;
|
|
5396
|
+
}
|
|
5387
5397
|
if ((phase === "orderBy" || phase === "having" || phase === "groupBy") && selectAliases.has(rawName)) {
|
|
5388
5398
|
return;
|
|
5389
5399
|
}
|
|
@@ -5544,9 +5554,9 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
5544
5554
|
return;
|
|
5545
5555
|
}
|
|
5546
5556
|
};
|
|
5547
|
-
const walkGroupByKey = (k) => {
|
|
5557
|
+
const walkGroupByKey = (k, resolution) => {
|
|
5548
5558
|
if (k.type === "FIELD_NAME") {
|
|
5549
|
-
addFieldName(k.name, "groupBy");
|
|
5559
|
+
addFieldName(k.name, "groupBy", resolution);
|
|
5550
5560
|
return;
|
|
5551
5561
|
}
|
|
5552
5562
|
if (k.type === "ARITH_KEY") {
|
|
@@ -5619,7 +5629,9 @@ function collectRequiredFieldsByTable(stmt) {
|
|
|
5619
5629
|
walkWhere(stmt.where, "where");
|
|
5620
5630
|
const grouping = normalizeGroupingSpec(stmt);
|
|
5621
5631
|
if (grouping.type === "PLAIN") {
|
|
5622
|
-
|
|
5632
|
+
grouping.allItems.forEach(
|
|
5633
|
+
(item, index) => walkGroupByKey(item, plainGroupByPlan?.items[index])
|
|
5634
|
+
);
|
|
5623
5635
|
} else if (grouping.type === "GROUPING_SETS") {
|
|
5624
5636
|
for (const item of grouping.allItems) {
|
|
5625
5637
|
addFieldRef(item.field, item.tableAlias, "select");
|
|
@@ -6853,8 +6865,8 @@ function parseOptionValues(value, fieldType) {
|
|
|
6853
6865
|
}
|
|
6854
6866
|
function optionVector(value, semantics) {
|
|
6855
6867
|
const order = semantics.optionOrder ?? /* @__PURE__ */ new Map();
|
|
6856
|
-
const
|
|
6857
|
-
const vector =
|
|
6868
|
+
const unique2 = [...new Set(parseOptionValues(value, semantics.fieldType))];
|
|
6869
|
+
const vector = unique2.map((label) => {
|
|
6858
6870
|
const rank = order.get(label);
|
|
6859
6871
|
return rank === void 0 ? { knownBand: 1, rank: 0, label } : { knownBand: 0, rank, label };
|
|
6860
6872
|
});
|
|
@@ -10802,10 +10814,10 @@ function planKorder(input) {
|
|
|
10802
10814
|
if (stmt.limit !== null && !Number.isSafeInteger(scanRows)) {
|
|
10803
10815
|
reasons.push(`KORDER_SCAN_ROWS_INVALID(offset=${offset}, limit=${stmt.limit})`);
|
|
10804
10816
|
}
|
|
10805
|
-
const
|
|
10806
|
-
if (
|
|
10817
|
+
const unique2 = [...new Set(reasons)];
|
|
10818
|
+
if (unique2.length > 0) {
|
|
10807
10819
|
throw new Error(
|
|
10808
|
-
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${
|
|
10820
|
+
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique2.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
|
|
10809
10821
|
);
|
|
10810
10822
|
}
|
|
10811
10823
|
const native = stmt.limit <= 500 && offset <= 1e4 && stmt.limit <= input.maxRecords;
|
|
@@ -10905,6 +10917,148 @@ function buildKorderCursorQuery(stmt) {
|
|
|
10905
10917
|
return parts.join(" ");
|
|
10906
10918
|
}
|
|
10907
10919
|
|
|
10920
|
+
// src/core/systemFields.ts
|
|
10921
|
+
var APP_SYSTEM_FIELD_CODES = [
|
|
10922
|
+
"$id",
|
|
10923
|
+
"$revision",
|
|
10924
|
+
"\u30EC\u30B3\u30FC\u30C9\u756A\u53F7",
|
|
10925
|
+
"\u4F5C\u6210\u8005",
|
|
10926
|
+
"\u4F5C\u6210\u65E5\u6642",
|
|
10927
|
+
"\u66F4\u65B0\u8005",
|
|
10928
|
+
"\u66F4\u65B0\u65E5\u6642",
|
|
10929
|
+
"\u30B9\u30C6\u30FC\u30BF\u30B9",
|
|
10930
|
+
"\u4F5C\u696D\u8005"
|
|
10931
|
+
];
|
|
10932
|
+
function isSystemLikeFieldCode(code) {
|
|
10933
|
+
return code.startsWith("_") || code.startsWith("$");
|
|
10934
|
+
}
|
|
10935
|
+
|
|
10936
|
+
// src/core/optimization/plainGroupByPlan.ts
|
|
10937
|
+
var AGGREGATE_REFERENCE_PREFIX = /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/;
|
|
10938
|
+
function containsAggregateColumnNode(node) {
|
|
10939
|
+
if (node === null || typeof node !== "object") return false;
|
|
10940
|
+
if (Array.isArray(node)) return node.some(containsAggregateColumnNode);
|
|
10941
|
+
const value = node;
|
|
10942
|
+
if (value["type"] === "AGGREGATE" || value["type"] === "ARITH_AGG_COL") return true;
|
|
10943
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
|
|
10944
|
+
if (value["type"] === "FIELD" && typeof value["field"] === "string") {
|
|
10945
|
+
return AGGREGATE_REFERENCE_PREFIX.test(value["field"]);
|
|
10946
|
+
}
|
|
10947
|
+
return Object.values(value).some(containsAggregateColumnNode);
|
|
10948
|
+
}
|
|
10949
|
+
function classifyPreGroupAlias(column) {
|
|
10950
|
+
switch (column.type) {
|
|
10951
|
+
case "AGGREGATE":
|
|
10952
|
+
case "ARITH_AGG_COL":
|
|
10953
|
+
return "AGGREGATE_DEPENDENT";
|
|
10954
|
+
case "GROUPING_COL":
|
|
10955
|
+
case "WINDOW_COL":
|
|
10956
|
+
return "POST_GROUP_ONLY";
|
|
10957
|
+
case "VARIABLE_COL":
|
|
10958
|
+
throw new Error("InternalError: unresolved VARIABLE_COL reached GROUP BY alias planning.");
|
|
10959
|
+
case "WILDCARD":
|
|
10960
|
+
case "PARENT_WILDCARD":
|
|
10961
|
+
case "FIELD":
|
|
10962
|
+
case "LITERAL_COL":
|
|
10963
|
+
case "ARITH_COL":
|
|
10964
|
+
case "CASE_COL":
|
|
10965
|
+
case "STRFUNC_COL":
|
|
10966
|
+
case "SCALAR_VALUE_COL":
|
|
10967
|
+
case "SCALAR_SUBQUERY_COL":
|
|
10968
|
+
return containsAggregate(column) || containsAggregateColumnNode(column) ? "AGGREGATE_DEPENDENT" : "SAFE";
|
|
10969
|
+
}
|
|
10970
|
+
}
|
|
10971
|
+
var SUBTABLE_SYSTEM_COLUMNS = ["_pid", "_rid", "_idx"];
|
|
10972
|
+
function unique(values) {
|
|
10973
|
+
return [...new Set(values)];
|
|
10974
|
+
}
|
|
10975
|
+
function sourceColumns(input) {
|
|
10976
|
+
switch (input.kind) {
|
|
10977
|
+
case "APP":
|
|
10978
|
+
return unique([
|
|
10979
|
+
...input.fieldCodes,
|
|
10980
|
+
...APP_SYSTEM_FIELD_CODES
|
|
10981
|
+
]);
|
|
10982
|
+
case "SUBTABLE":
|
|
10983
|
+
return unique([
|
|
10984
|
+
...input.childFieldCodes,
|
|
10985
|
+
...SUBTABLE_SYSTEM_COLUMNS,
|
|
10986
|
+
...input.parentFieldCodes.map((field) => `_p.${field}`)
|
|
10987
|
+
]);
|
|
10988
|
+
case "MATERIALIZED":
|
|
10989
|
+
return unique(input.columns);
|
|
10990
|
+
}
|
|
10991
|
+
}
|
|
10992
|
+
function resolvePlainGroupBySourceSchemas(stmt, lookup) {
|
|
10993
|
+
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
10994
|
+
return sources.map((source, sourceIndex) => ({
|
|
10995
|
+
sourceIndex,
|
|
10996
|
+
qualifier: source.alias ?? source.cteName,
|
|
10997
|
+
columns: sourceColumns(lookup(source, sourceIndex))
|
|
10998
|
+
}));
|
|
10999
|
+
}
|
|
11000
|
+
function parseGroupName(name) {
|
|
11001
|
+
if (name.startsWith("_p.")) return { qualifier: null, fieldCode: name };
|
|
11002
|
+
const dot = name.indexOf(".");
|
|
11003
|
+
if (dot <= 0 || dot === name.length - 1) {
|
|
11004
|
+
return { qualifier: null, fieldCode: name };
|
|
11005
|
+
}
|
|
11006
|
+
return { qualifier: name.slice(0, dot), fieldCode: name.slice(dot + 1) };
|
|
11007
|
+
}
|
|
11008
|
+
function runtimeKey(source, fieldCode) {
|
|
11009
|
+
return source.qualifier === null ? fieldCode : `${source.qualifier}.${fieldCode}`;
|
|
11010
|
+
}
|
|
11011
|
+
function explicitAlias(column) {
|
|
11012
|
+
return "alias" in column && typeof column.alias === "string" ? column.alias : null;
|
|
11013
|
+
}
|
|
11014
|
+
function resolveFieldName(name, columns, schemas) {
|
|
11015
|
+
const parsed = parseGroupName(name);
|
|
11016
|
+
const candidateSources = parsed.qualifier === null ? schemas : schemas.filter((source) => source.qualifier === parsed.qualifier);
|
|
11017
|
+
const physical = candidateSources.filter((source) => source.columns.includes(parsed.fieldCode));
|
|
11018
|
+
if (physical.length > 1) {
|
|
11019
|
+
throw new Error(
|
|
11020
|
+
`ArgumentError: GROUP BY field ${name} is ambiguous across multiple sources (reason=GROUP_BY_FIELD_AMBIGUOUS).`
|
|
11021
|
+
);
|
|
11022
|
+
}
|
|
11023
|
+
if (physical.length === 1) {
|
|
11024
|
+
const source = physical[0];
|
|
11025
|
+
return {
|
|
11026
|
+
kind: "PHYSICAL",
|
|
11027
|
+
sourceIndex: source.sourceIndex,
|
|
11028
|
+
fieldCode: parsed.fieldCode,
|
|
11029
|
+
runtimeKey: runtimeKey(source, parsed.fieldCode)
|
|
11030
|
+
};
|
|
11031
|
+
}
|
|
11032
|
+
if (parsed.qualifier !== null) return { kind: "UNKNOWN", name };
|
|
11033
|
+
const aliases = columns.flatMap(
|
|
11034
|
+
(column, columnIndex) => explicitAlias(column) === name ? [{ column, columnIndex }] : []
|
|
11035
|
+
);
|
|
11036
|
+
if (aliases.length > 1) return { kind: "ALIAS_REJECT", reason: "DUPLICATE" };
|
|
11037
|
+
if (aliases.length === 1) {
|
|
11038
|
+
const candidate = aliases[0];
|
|
11039
|
+
const classification = classifyPreGroupAlias(candidate.column);
|
|
11040
|
+
if (classification === "SAFE") {
|
|
11041
|
+
return { kind: "ALIAS_SAFE", columnIndex: candidate.columnIndex };
|
|
11042
|
+
}
|
|
11043
|
+
return {
|
|
11044
|
+
kind: "ALIAS_REJECT",
|
|
11045
|
+
reason: classification === "AGGREGATE_DEPENDENT" ? "AGGREGATE" : "POST_GROUP_ONLY"
|
|
11046
|
+
};
|
|
11047
|
+
}
|
|
11048
|
+
const aggregateSyntheticMatch = columns.some(
|
|
11049
|
+
(column) => column.type === "AGGREGATE" && column.alias === null && aggregateSyntheticName(column.func, column.distinct, column.arg) === name
|
|
11050
|
+
);
|
|
11051
|
+
if (aggregateSyntheticMatch) return { kind: "ALIAS_REJECT", reason: "AGGREGATE" };
|
|
11052
|
+
return { kind: "UNKNOWN", name };
|
|
11053
|
+
}
|
|
11054
|
+
function planPlainGroupByResolution(groupBy, columns, schemas) {
|
|
11055
|
+
return {
|
|
11056
|
+
items: groupBy.map(
|
|
11057
|
+
(key) => key.type === "FIELD_NAME" ? resolveFieldName(key.name, columns, schemas) : { kind: "EXPRESSION" }
|
|
11058
|
+
)
|
|
11059
|
+
};
|
|
11060
|
+
}
|
|
11061
|
+
|
|
10908
11062
|
// src/engine/process.ts
|
|
10909
11063
|
function flatten(record, alias) {
|
|
10910
11064
|
const row = {};
|
|
@@ -10986,10 +11140,21 @@ function hasAggregateColumns(columns) {
|
|
|
10986
11140
|
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(c.expr)
|
|
10987
11141
|
);
|
|
10988
11142
|
}
|
|
10989
|
-
function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
|
|
11143
|
+
function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind, resolutionPlan, aliasEvaluationContext = {}) {
|
|
11144
|
+
if (resolutionPlan && resolutionPlan.items.length !== groupByKeys.length) {
|
|
11145
|
+
throw new Error("InternalError: plain GROUP BY resolution plan length does not match group keys.");
|
|
11146
|
+
}
|
|
10990
11147
|
const groups = /* @__PURE__ */ new Map();
|
|
10991
11148
|
for (const row of rows) {
|
|
10992
|
-
const key = groupByKeys.map(
|
|
11149
|
+
const key = groupByKeys.map(
|
|
11150
|
+
(groupKey, index) => evalGroupByKey(
|
|
11151
|
+
groupKey,
|
|
11152
|
+
row,
|
|
11153
|
+
resolutionPlan?.items[index],
|
|
11154
|
+
columns,
|
|
11155
|
+
aliasEvaluationContext
|
|
11156
|
+
)
|
|
11157
|
+
).join("\0");
|
|
10993
11158
|
const bucket = groups.get(key);
|
|
10994
11159
|
if (bucket) bucket.push(row);
|
|
10995
11160
|
else groups.set(key, [row]);
|
|
@@ -11097,8 +11262,32 @@ function materializeAggregateColumns(outRow, groupRows, columns, resolveAggSortK
|
|
|
11097
11262
|
}
|
|
11098
11263
|
}
|
|
11099
11264
|
}
|
|
11100
|
-
function evalGroupByKey(key, row) {
|
|
11101
|
-
if (key.type === "FIELD_NAME")
|
|
11265
|
+
function evalGroupByKey(key, row, resolution, columns, aliasEvaluationContext) {
|
|
11266
|
+
if (key.type === "FIELD_NAME") {
|
|
11267
|
+
if (!resolution) return row[key.name] ?? "";
|
|
11268
|
+
if (resolution.kind === "PHYSICAL") return row[resolution.runtimeKey] ?? "";
|
|
11269
|
+
if (resolution.kind === "ALIAS_SAFE") {
|
|
11270
|
+
const column = columns[resolution.columnIndex];
|
|
11271
|
+
if (!column) {
|
|
11272
|
+
throw new Error(
|
|
11273
|
+
`InternalError: GROUP BY alias column index ${resolution.columnIndex} is out of range.`
|
|
11274
|
+
);
|
|
11275
|
+
}
|
|
11276
|
+
const value = evaluateSelectColumnValue(
|
|
11277
|
+
column,
|
|
11278
|
+
row,
|
|
11279
|
+
resolution.columnIndex,
|
|
11280
|
+
aliasEvaluationContext
|
|
11281
|
+
);
|
|
11282
|
+
if (typeof value !== "string") {
|
|
11283
|
+
throw new Error("InternalError: GROUP BY alias resolved to an expanded SELECT column.");
|
|
11284
|
+
}
|
|
11285
|
+
return value;
|
|
11286
|
+
}
|
|
11287
|
+
throw new Error(
|
|
11288
|
+
`InternalError: unresolved plain GROUP BY item ${resolution.kind} reached evaluation.`
|
|
11289
|
+
);
|
|
11290
|
+
}
|
|
11102
11291
|
if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
|
|
11103
11292
|
return String(evalArithExpr(key.expr, row));
|
|
11104
11293
|
}
|
|
@@ -11536,7 +11725,7 @@ function buildDistinctTuple(columns, row, context = {}) {
|
|
|
11536
11725
|
return typeof value === "string" ? value : value.entries.map(([, entryValue]) => entryValue);
|
|
11537
11726
|
});
|
|
11538
11727
|
}
|
|
11539
|
-
function project(rows, columns, scalarCache, resolveFieldType,
|
|
11728
|
+
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns2, resolveFieldSemantics2, hiddenQualifiedAliases) {
|
|
11540
11729
|
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
11541
11730
|
const projected2 = rows.map((row) => {
|
|
11542
11731
|
const visible = stripHiddenQualifiedColumns(
|
|
@@ -11554,7 +11743,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
|
|
|
11554
11743
|
}
|
|
11555
11744
|
return out;
|
|
11556
11745
|
});
|
|
11557
|
-
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...
|
|
11746
|
+
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns2 ?? []];
|
|
11558
11747
|
return { rows: projected2, columns: cols };
|
|
11559
11748
|
}
|
|
11560
11749
|
const defaultFieldKeys = buildDefaultFieldOutputKeys(columns);
|
|
@@ -11898,10 +12087,11 @@ function runFullScan(input) {
|
|
|
11898
12087
|
havingFieldSemanticsResolver,
|
|
11899
12088
|
aggregateSortKindResolver,
|
|
11900
12089
|
appliedKlikes,
|
|
11901
|
-
sourceColumns,
|
|
12090
|
+
sourceColumns: sourceColumns2,
|
|
11902
12091
|
tableColumns,
|
|
11903
12092
|
hiddenQualifiedAliases,
|
|
11904
|
-
resolvedGroupingSpec
|
|
12093
|
+
resolvedGroupingSpec,
|
|
12094
|
+
plainGroupByPlan
|
|
11905
12095
|
} = input;
|
|
11906
12096
|
const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
|
|
11907
12097
|
for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
|
|
@@ -11948,7 +12138,13 @@ function runFullScan(input) {
|
|
|
11948
12138
|
rows,
|
|
11949
12139
|
grouping.type === "PLAIN" ? grouping.allItems : [],
|
|
11950
12140
|
stmt.columns,
|
|
11951
|
-
aggregateSortKindResolver
|
|
12141
|
+
aggregateSortKindResolver,
|
|
12142
|
+
grouping.type === "PLAIN" ? plainGroupByPlan : void 0,
|
|
12143
|
+
{
|
|
12144
|
+
scalarCache,
|
|
12145
|
+
resolveFieldType: fieldTypeResolver,
|
|
12146
|
+
resolveFieldSemantics: fieldSemanticsResolver
|
|
12147
|
+
}
|
|
11952
12148
|
);
|
|
11953
12149
|
}
|
|
11954
12150
|
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
|
|
@@ -11976,7 +12172,7 @@ function runFullScan(input) {
|
|
|
11976
12172
|
stmt.columns,
|
|
11977
12173
|
scalarCache,
|
|
11978
12174
|
fieldTypeResolver,
|
|
11979
|
-
|
|
12175
|
+
sourceColumns2,
|
|
11980
12176
|
fieldSemanticsResolver,
|
|
11981
12177
|
hiddenQualifiedAliases
|
|
11982
12178
|
);
|
|
@@ -15229,6 +15425,12 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15229
15425
|
}
|
|
15230
15426
|
return result;
|
|
15231
15427
|
}
|
|
15428
|
+
const plainGroupByPlan = await buildRuntimePlainGroupByPlan(
|
|
15429
|
+
stmt,
|
|
15430
|
+
client,
|
|
15431
|
+
cacheContext,
|
|
15432
|
+
cteCache
|
|
15433
|
+
);
|
|
15232
15434
|
await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
|
|
15233
15435
|
const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
|
|
15234
15436
|
if (whereCapability.capability === "UNSUPPORTED") {
|
|
@@ -15273,7 +15475,8 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15273
15475
|
cteCache,
|
|
15274
15476
|
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
15275
15477
|
orderMeta,
|
|
15276
|
-
prefilterPlan
|
|
15478
|
+
prefilterPlan,
|
|
15479
|
+
plainGroupByPlan
|
|
15277
15480
|
);
|
|
15278
15481
|
}
|
|
15279
15482
|
} catch (error) {
|
|
@@ -15287,6 +15490,78 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15287
15490
|
}
|
|
15288
15491
|
return result;
|
|
15289
15492
|
}
|
|
15493
|
+
async function buildRuntimePlainGroupByPlan(stmt, client, cacheContext, materializedTables) {
|
|
15494
|
+
if (stmt.groupBy.length === 0) return void 0;
|
|
15495
|
+
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
15496
|
+
const inputs = await Promise.all(sources.map(async (source) => {
|
|
15497
|
+
if (source.cteName !== null) {
|
|
15498
|
+
const materialized = materializedTables?.get(source.cteName);
|
|
15499
|
+
if (!materialized) {
|
|
15500
|
+
throw new Error(`InternalError: materialized schema ${source.cteName} is not available for GROUP BY planning.`);
|
|
15501
|
+
}
|
|
15502
|
+
return { kind: "MATERIALIZED", columns: materialized.columns };
|
|
15503
|
+
}
|
|
15504
|
+
const fields = await getFieldsCached(source.appId, client, cacheContext);
|
|
15505
|
+
if (!source.subtableCode) {
|
|
15506
|
+
return {
|
|
15507
|
+
kind: "APP",
|
|
15508
|
+
fieldCodes: fields.filter((field) => !field.inSubtable).map((field) => field.code)
|
|
15509
|
+
};
|
|
15510
|
+
}
|
|
15511
|
+
const exactChildren = fields.filter(
|
|
15512
|
+
(field) => field.inSubtable && field.subtableCode === source.subtableCode
|
|
15513
|
+
);
|
|
15514
|
+
const childFields = exactChildren.length > 0 ? exactChildren : fields.filter((field) => field.inSubtable && field.subtableCode === void 0);
|
|
15515
|
+
return {
|
|
15516
|
+
kind: "SUBTABLE",
|
|
15517
|
+
childFieldCodes: childFields.map((field) => field.code),
|
|
15518
|
+
parentFieldCodes: fields.filter((field) => !field.inSubtable).map((field) => field.code)
|
|
15519
|
+
};
|
|
15520
|
+
}));
|
|
15521
|
+
const schemas = resolvePlainGroupBySourceSchemas(
|
|
15522
|
+
stmt,
|
|
15523
|
+
(_source, sourceIndex) => inputs[sourceIndex]
|
|
15524
|
+
);
|
|
15525
|
+
const groupBy = stmt.groupBy;
|
|
15526
|
+
const plan = planPlainGroupByResolution(groupBy, stmt.columns, schemas);
|
|
15527
|
+
assertRuntimePlainGroupByPlan(stmt, groupBy, plan);
|
|
15528
|
+
return plan;
|
|
15529
|
+
}
|
|
15530
|
+
function assertRuntimePlainGroupByPlan(stmt, groupBy, plan) {
|
|
15531
|
+
const physicalAppIds = [
|
|
15532
|
+
stmt.from,
|
|
15533
|
+
...stmt.joins.map((join2) => join2.table)
|
|
15534
|
+
].flatMap((source) => source.cteName === null ? [source.appId] : []);
|
|
15535
|
+
const uniquePhysicalAppIds = [...new Set(physicalAppIds)];
|
|
15536
|
+
const primaryPhysicalAppId = uniquePhysicalAppIds.length === 1 ? uniquePhysicalAppIds[0] : stmt.from.cteName === null ? stmt.from.appId : null;
|
|
15537
|
+
plan.items.forEach((item, index) => {
|
|
15538
|
+
if (item.kind === "EXPRESSION" || item.kind === "PHYSICAL" || item.kind === "ALIAS_SAFE") return;
|
|
15539
|
+
const key = groupBy[index];
|
|
15540
|
+
const name = key?.type === "FIELD_NAME" ? key.name : "(expression)";
|
|
15541
|
+
if (item.kind === "UNKNOWN") {
|
|
15542
|
+
const appSuffix = primaryPhysicalAppId === null ? "" : ` (APP${primaryPhysicalAppId})`;
|
|
15543
|
+
throw new Error(`ArgumentError: unknown field code(s): ${item.name}${appSuffix}`);
|
|
15544
|
+
}
|
|
15545
|
+
if (item.kind === "ALIAS_REJECT") {
|
|
15546
|
+
if (item.reason === "DUPLICATE") {
|
|
15547
|
+
throw new Error(
|
|
15548
|
+
`ArgumentError: GROUP BY alias ${name} is ambiguous across multiple SELECT columns (reason=GROUP_BY_ALIAS_AMBIGUOUS).`
|
|
15549
|
+
);
|
|
15550
|
+
}
|
|
15551
|
+
if (item.reason === "POST_GROUP_ONLY") {
|
|
15552
|
+
throw new Error(
|
|
15553
|
+
`ArgumentError: GROUP BY alias ${name} requires post-group evaluation (reason=GROUP_BY_ALIAS_POST_GROUP_ONLY).`
|
|
15554
|
+
);
|
|
15555
|
+
}
|
|
15556
|
+
throw new Error(
|
|
15557
|
+
`ArgumentError: GROUP BY alias ${name} depends on aggregate evaluation (reason=GROUP_BY_ALIAS_AGGREGATE).`
|
|
15558
|
+
);
|
|
15559
|
+
}
|
|
15560
|
+
throw new Error(
|
|
15561
|
+
`InternalError: deferred GROUP BY field ${item.name} reached runtime planning.`
|
|
15562
|
+
);
|
|
15563
|
+
});
|
|
15564
|
+
}
|
|
15290
15565
|
var resolvedGroupingSpecs = /* @__PURE__ */ new WeakMap();
|
|
15291
15566
|
async function validateStatementGroupingPlanning(statement, client, cacheContext) {
|
|
15292
15567
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -16218,7 +16493,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
16218
16493
|
};
|
|
16219
16494
|
return { row, having };
|
|
16220
16495
|
}
|
|
16221
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta, prefilterPlan) {
|
|
16496
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta, prefilterPlan, plainGroupByPlan) {
|
|
16222
16497
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
16223
16498
|
const warnings = /* @__PURE__ */ new Set();
|
|
16224
16499
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -16248,6 +16523,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16248
16523
|
throw new Error("internal error: relative-date prefilter must disable original WHERE pushdown.");
|
|
16249
16524
|
}
|
|
16250
16525
|
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : mainPushDown;
|
|
16526
|
+
const scalarCache = await resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
16251
16527
|
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
16252
16528
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
16253
16529
|
stmt,
|
|
@@ -16259,7 +16535,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16259
16535
|
options.onLimitReached ?? "error",
|
|
16260
16536
|
warnings,
|
|
16261
16537
|
mainFetchCondition,
|
|
16262
|
-
allowOriginalWherePushdown
|
|
16538
|
+
allowOriginalWherePushdown,
|
|
16539
|
+
plainGroupByPlan
|
|
16263
16540
|
);
|
|
16264
16541
|
const parallelJoins = [];
|
|
16265
16542
|
const onOptJoins = [];
|
|
@@ -16281,17 +16558,16 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16281
16558
|
false,
|
|
16282
16559
|
options.onLimitReached ?? "error",
|
|
16283
16560
|
warnings,
|
|
16284
|
-
jCond
|
|
16561
|
+
jCond,
|
|
16562
|
+
true,
|
|
16563
|
+
plainGroupByPlan
|
|
16285
16564
|
)
|
|
16286
16565
|
});
|
|
16287
16566
|
} else {
|
|
16288
16567
|
onOptJoins.push(join2);
|
|
16289
16568
|
}
|
|
16290
16569
|
}
|
|
16291
|
-
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
16292
16570
|
const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
16293
|
-
scalarCachePromise.catch(() => {
|
|
16294
|
-
});
|
|
16295
16571
|
orderByMetaPromise.catch(() => {
|
|
16296
16572
|
});
|
|
16297
16573
|
const mainRecords = await mainFetch;
|
|
@@ -16310,7 +16586,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16310
16586
|
parallel,
|
|
16311
16587
|
options.onLimitReached ?? "error",
|
|
16312
16588
|
warnings,
|
|
16313
|
-
null
|
|
16589
|
+
null,
|
|
16590
|
+
plainGroupByPlan
|
|
16314
16591
|
);
|
|
16315
16592
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
16316
16593
|
stmt,
|
|
@@ -16321,11 +16598,12 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16321
16598
|
false,
|
|
16322
16599
|
options.onLimitReached ?? "error",
|
|
16323
16600
|
warnings,
|
|
16324
|
-
null
|
|
16601
|
+
null,
|
|
16602
|
+
true,
|
|
16603
|
+
plainGroupByPlan
|
|
16325
16604
|
);
|
|
16326
16605
|
tables.set(join2.table.alias, joinRecords);
|
|
16327
16606
|
}));
|
|
16328
|
-
const scalarCache = await scalarCachePromise;
|
|
16329
16607
|
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
16330
16608
|
const { rows, columns } = runFullScan({
|
|
16331
16609
|
tables,
|
|
@@ -16341,7 +16619,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16341
16619
|
aggregateSortKindResolver,
|
|
16342
16620
|
appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
|
|
16343
16621
|
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : {},
|
|
16344
|
-
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt)
|
|
16622
|
+
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt),
|
|
16623
|
+
plainGroupByPlan
|
|
16345
16624
|
});
|
|
16346
16625
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
16347
16626
|
}
|
|
@@ -16434,6 +16713,12 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
|
|
|
16434
16713
|
async function executeFullScanWithCte(stmt, client, options, cteCache, cacheContext) {
|
|
16435
16714
|
await validateSelectGroupingPlanning(stmt, client, cacheContext, cteCache);
|
|
16436
16715
|
const resolvedGroupingSpec = resolvedGroupingSpecs.get(stmt);
|
|
16716
|
+
const plainGroupByPlan = await buildRuntimePlainGroupByPlan(
|
|
16717
|
+
stmt,
|
|
16718
|
+
client,
|
|
16719
|
+
cacheContext,
|
|
16720
|
+
cteCache
|
|
16721
|
+
);
|
|
16437
16722
|
const hiddenQualifiedAliases = /* @__PURE__ */ new Set();
|
|
16438
16723
|
const withEffectiveAlias = (table) => {
|
|
16439
16724
|
if (table.alias !== null || table.cteName === null) return table;
|
|
@@ -16505,10 +16790,14 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16505
16790
|
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
16506
16791
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
16507
16792
|
validateKlikePushdownPlan(pushdownPlan);
|
|
16508
|
-
const
|
|
16793
|
+
const scalarCache = await resolveScalarColumns(
|
|
16794
|
+
stmt.columns,
|
|
16795
|
+
client,
|
|
16796
|
+
effectiveOptions,
|
|
16797
|
+
cacheContext,
|
|
16798
|
+
cteCache
|
|
16799
|
+
);
|
|
16509
16800
|
const orderByMetaPromise = Promise.resolve(orderMeta);
|
|
16510
|
-
scalarCachePromise.catch(() => {
|
|
16511
|
-
});
|
|
16512
16801
|
orderByMetaPromise.catch(() => {
|
|
16513
16802
|
});
|
|
16514
16803
|
const tables = /* @__PURE__ */ new Map();
|
|
@@ -16528,7 +16817,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16528
16817
|
effectiveOptions.onLimitReached ?? "error",
|
|
16529
16818
|
warnings,
|
|
16530
16819
|
pushdownPlan.mainCondition,
|
|
16531
|
-
whereCapability.capability === "EXACT_PUSHDOWN"
|
|
16820
|
+
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
16821
|
+
plainGroupByPlan
|
|
16532
16822
|
));
|
|
16533
16823
|
tables.set(stmt.from.alias, mainRecords);
|
|
16534
16824
|
}
|
|
@@ -16548,7 +16838,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16548
16838
|
parallel,
|
|
16549
16839
|
effectiveOptions.onLimitReached ?? "error",
|
|
16550
16840
|
warnings,
|
|
16551
|
-
pushDownCond
|
|
16841
|
+
pushDownCond,
|
|
16842
|
+
plainGroupByPlan
|
|
16552
16843
|
);
|
|
16553
16844
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
16554
16845
|
stmt,
|
|
@@ -16559,15 +16850,16 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16559
16850
|
false,
|
|
16560
16851
|
effectiveOptions.onLimitReached ?? "error",
|
|
16561
16852
|
warnings,
|
|
16562
|
-
pushDownCond
|
|
16853
|
+
pushDownCond,
|
|
16854
|
+
true,
|
|
16855
|
+
plainGroupByPlan
|
|
16563
16856
|
);
|
|
16564
16857
|
tables.set(join2.table.alias, joinRecords);
|
|
16565
16858
|
}
|
|
16566
16859
|
}));
|
|
16567
16860
|
await Promise.all(joinFetches);
|
|
16568
|
-
const scalarCache = await scalarCachePromise;
|
|
16569
16861
|
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
16570
|
-
const
|
|
16862
|
+
const sourceColumns2 = stmt.joins.length === 0 && stmt.from.cteName != null ? requireMaterializedTable(stmt.from.cteName).columns : void 0;
|
|
16571
16863
|
const { rows, columns } = runFullScan({
|
|
16572
16864
|
tables,
|
|
16573
16865
|
stmt,
|
|
@@ -16581,10 +16873,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16581
16873
|
havingFieldSemanticsResolver,
|
|
16582
16874
|
aggregateSortKindResolver,
|
|
16583
16875
|
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
16584
|
-
sourceColumns,
|
|
16876
|
+
sourceColumns: sourceColumns2,
|
|
16585
16877
|
tableColumns,
|
|
16586
16878
|
hiddenQualifiedAliases,
|
|
16587
|
-
resolvedGroupingSpec
|
|
16879
|
+
resolvedGroupingSpec,
|
|
16880
|
+
plainGroupByPlan
|
|
16588
16881
|
});
|
|
16589
16882
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
16590
16883
|
}
|
|
@@ -16593,8 +16886,8 @@ function processRowToKintoneRecord(row) {
|
|
|
16593
16886
|
Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
|
|
16594
16887
|
);
|
|
16595
16888
|
}
|
|
16596
|
-
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true) {
|
|
16597
|
-
const fields = selectToFetchAllFields(stmt, table);
|
|
16889
|
+
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true, plainGroupByPlan) {
|
|
16890
|
+
const fields = selectToFetchAllFields(stmt, table, plainGroupByPlan);
|
|
16598
16891
|
const onTruncate = (max) => {
|
|
16599
16892
|
warnings.add(`\u53D6\u5F97\u4E0A\u9650\uFF08${max} \u4EF6\uFF09\u306B\u9054\u3057\u305F\u305F\u3081\u3001${max} \u4EF6\u3067\u6253\u3061\u5207\u3063\u3066\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002`);
|
|
16600
16893
|
};
|
|
@@ -16708,7 +17001,7 @@ function splitChunks(items, size) {
|
|
|
16708
17001
|
var JOIN_IN_CHUNK_SIZE = 50;
|
|
16709
17002
|
var JOIN_IN_MAX_CHUNKS = 6;
|
|
16710
17003
|
var JOIN_IN_MAX_KEYS = JOIN_IN_CHUNK_SIZE * JOIN_IN_MAX_CHUNKS;
|
|
16711
|
-
async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxRecords, parallel, onLimit, warnings, pushDownCond = null) {
|
|
17004
|
+
async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxRecords, parallel, onLimit, warnings, pushDownCond = null, plainGroupByPlan) {
|
|
16712
17005
|
if (join2.type !== "INNER") return null;
|
|
16713
17006
|
if (!join2.table.alias) return null;
|
|
16714
17007
|
if (join2.table.subtableCode) return null;
|
|
@@ -16745,7 +17038,7 @@ async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxR
|
|
|
16745
17038
|
);
|
|
16746
17039
|
return null;
|
|
16747
17040
|
}
|
|
16748
|
-
const fields = selectToFetchAllFields(stmt, join2.table);
|
|
17041
|
+
const fields = selectToFetchAllFields(stmt, join2.table, plainGroupByPlan);
|
|
16749
17042
|
const onTruncate = (max) => {
|
|
16750
17043
|
warnings.add(`\u53D6\u5F97\u4E0A\u9650\uFF08${max} \u4EF6\uFF09\u306B\u9054\u3057\u305F\u305F\u3081\u3001${max} \u4EF6\u3067\u6253\u3061\u5207\u3063\u3066\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002`);
|
|
16751
17044
|
};
|
|
@@ -16787,9 +17080,6 @@ function setScopedCacheValue(root, cacheContext, appId, value) {
|
|
|
16787
17080
|
}
|
|
16788
17081
|
scoped.set(appId, value);
|
|
16789
17082
|
}
|
|
16790
|
-
function isSystemLikeFieldCode(code) {
|
|
16791
|
-
return code.startsWith("_") || code.startsWith("$");
|
|
16792
|
-
}
|
|
16793
17083
|
async function getFieldsCached(appId, client, cacheContext) {
|
|
16794
17084
|
const cached = getScopedCacheValue(fieldInfoCache, cacheContext, appId);
|
|
16795
17085
|
if (cached) return cached;
|
|
@@ -19985,6 +20275,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
19985
20275
|
};
|
|
19986
20276
|
const capabilities = /* @__PURE__ */ new Map();
|
|
19987
20277
|
const orderPlans = /* @__PURE__ */ new Map();
|
|
20278
|
+
const plainGroupByPlans = /* @__PURE__ */ new Map();
|
|
19988
20279
|
const seen = /* @__PURE__ */ new Set();
|
|
19989
20280
|
const sharedRelativeDatePlan = relativeDatePlan ?? await resolveRelativeDateExecutionPlan(query, tracedClient, cacheContext);
|
|
19990
20281
|
const relativeNodeFor = (source) => sharedRelativeDatePlan.nodes.find(
|
|
@@ -20002,6 +20293,15 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
20002
20293
|
if (typed["type"] === "SELECT") {
|
|
20003
20294
|
const select = node;
|
|
20004
20295
|
await validateSelectGroupingPlanning(select, tracedClient, cacheContext);
|
|
20296
|
+
const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
|
|
20297
|
+
if (normalizeGroupingSpec(select).type === "PLAIN" && !hasUnmaterializedSource) {
|
|
20298
|
+
const plainPlan = await buildRuntimePlainGroupByPlan(
|
|
20299
|
+
select,
|
|
20300
|
+
tracedClient,
|
|
20301
|
+
cacheContext
|
|
20302
|
+
);
|
|
20303
|
+
if (plainPlan) plainGroupByPlans.set(select, plainPlan);
|
|
20304
|
+
}
|
|
20005
20305
|
const physicalApps = [select.from, ...select.joins.map((join2) => join2.table)].filter((table) => table.cteName === null).map((table) => table.appId);
|
|
20006
20306
|
const needsWhereSchema = whereNeedsFieldMetadata(select.where);
|
|
20007
20307
|
if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
@@ -20022,7 +20322,6 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
20022
20322
|
}
|
|
20023
20323
|
}
|
|
20024
20324
|
}
|
|
20025
|
-
const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
|
|
20026
20325
|
if (hasCanonicalOrder(select) && !hasUnmaterializedSource && relativeNode?.allowed !== false) {
|
|
20027
20326
|
const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
|
|
20028
20327
|
orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
@@ -20136,6 +20435,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
20136
20435
|
return {
|
|
20137
20436
|
capabilities,
|
|
20138
20437
|
orderPlans,
|
|
20438
|
+
plainGroupByPlans,
|
|
20139
20439
|
fieldApps,
|
|
20140
20440
|
processStatusApps,
|
|
20141
20441
|
numberPrecisionApps,
|
|
@@ -20459,7 +20759,8 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
|
|
|
20459
20759
|
analysis.orderPlans,
|
|
20460
20760
|
dmlMaxRows,
|
|
20461
20761
|
dmlMaxSubtableRows,
|
|
20462
|
-
maxRecords
|
|
20762
|
+
maxRecords,
|
|
20763
|
+
analysis.plainGroupByPlans
|
|
20463
20764
|
),
|
|
20464
20765
|
cursorMaxActive
|
|
20465
20766
|
)
|
|
@@ -20482,13 +20783,13 @@ function addCursorConcurrency(lines, cursorMaxActive) {
|
|
|
20482
20783
|
}
|
|
20483
20784
|
return result;
|
|
20484
20785
|
}
|
|
20485
|
-
function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
|
|
20486
|
-
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
|
|
20487
|
-
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
|
|
20786
|
+
function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4, plainGroupByPlans) {
|
|
20787
|
+
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans, plainGroupByPlans);
|
|
20788
|
+
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans, plainGroupByPlans);
|
|
20488
20789
|
if (query.type === "INSERT") return buildInsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
20489
|
-
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
|
|
20790
|
+
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
|
|
20490
20791
|
if (query.type === "UPSERT") return buildUpsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
20491
|
-
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans);
|
|
20792
|
+
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
|
|
20492
20793
|
if (query.type === "UPDATE") return buildUpdatePlan(
|
|
20493
20794
|
query,
|
|
20494
20795
|
label,
|
|
@@ -20574,7 +20875,7 @@ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 1
|
|
|
20574
20875
|
` duplicateKey: preflight before lookup/write (requires load)`
|
|
20575
20876
|
];
|
|
20576
20877
|
}
|
|
20577
|
-
return buildSelectPlan(query, label, capabilities, orderPlans);
|
|
20878
|
+
return buildSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
|
|
20578
20879
|
}
|
|
20579
20880
|
function buildValidatePlan(stmt, label) {
|
|
20580
20881
|
const info = validateExplainInfo.get(stmt);
|
|
@@ -20601,9 +20902,10 @@ function buildValidatePlan(stmt, label) {
|
|
|
20601
20902
|
lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
|
|
20602
20903
|
return lines;
|
|
20603
20904
|
}
|
|
20604
|
-
function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
20905
|
+
function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans) {
|
|
20605
20906
|
const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
20606
20907
|
const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
20908
|
+
const plainGroupByPlan = plainGroupByPlans?.get(stmt) ?? (plainGroupByPlans ? [...plainGroupByPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
20607
20909
|
const mode = orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN" ? "FULL_SCAN" : resolveSelectMode(stmt);
|
|
20608
20910
|
const reasons = collectFullScanReasons(stmt);
|
|
20609
20911
|
if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
|
|
@@ -20628,6 +20930,29 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20628
20930
|
` grouping output rows: runtime checked (limit: ${groupingMetadata.outputRowLimit}, before HAVING/DISTINCT/LIMIT)`
|
|
20629
20931
|
);
|
|
20630
20932
|
}
|
|
20933
|
+
const normalizedGrouping = normalizeGroupingSpec(stmt);
|
|
20934
|
+
if (normalizedGrouping.type === "PLAIN") {
|
|
20935
|
+
const groupBy = normalizedGrouping.allItems;
|
|
20936
|
+
if (plainGroupByPlan) {
|
|
20937
|
+
plainGroupByPlan.items.forEach((item, index) => {
|
|
20938
|
+
const key = groupBy[index];
|
|
20939
|
+
if (key?.type !== "FIELD_NAME") return;
|
|
20940
|
+
if (item.kind === "PHYSICAL") {
|
|
20941
|
+
lines.push(
|
|
20942
|
+
` group key ${key.name}: PHYSICAL (source=${item.sourceIndex}, field=${item.fieldCode})`
|
|
20943
|
+
);
|
|
20944
|
+
}
|
|
20945
|
+
});
|
|
20946
|
+
} else if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) {
|
|
20947
|
+
for (const key of groupBy) {
|
|
20948
|
+
if (key.type === "FIELD_NAME") {
|
|
20949
|
+
lines.push(
|
|
20950
|
+
` group key ${key.name}: DEFERRED (materialized schema unavailable)`
|
|
20951
|
+
);
|
|
20952
|
+
}
|
|
20953
|
+
}
|
|
20954
|
+
}
|
|
20955
|
+
}
|
|
20631
20956
|
if (orderPlan) {
|
|
20632
20957
|
lines.push(` order plan: ${orderPlan.kind}`);
|
|
20633
20958
|
if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
|
|
@@ -20669,7 +20994,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20669
20994
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
20670
20995
|
} else {
|
|
20671
20996
|
const pushdownPlan = buildKlikePushdownPlan(stmt);
|
|
20672
|
-
const mainFields = selectToFetchAllFields(stmt, stmt.from);
|
|
20997
|
+
const mainFields = selectToFetchAllFields(stmt, stmt.from, plainGroupByPlan);
|
|
20673
20998
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
20674
20999
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
20675
21000
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
@@ -20682,7 +21007,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20682
21007
|
}
|
|
20683
21008
|
lines.push(` fields: ${mainFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : mainFields.join(", ")}`);
|
|
20684
21009
|
for (const join2 of stmt.joins) {
|
|
20685
|
-
const joinFields = selectToFetchAllFields(stmt, join2.table);
|
|
21010
|
+
const joinFields = selectToFetchAllFields(stmt, join2.table, plainGroupByPlan);
|
|
20686
21011
|
const joinAliasStr = join2.table.alias ? ` AS ${join2.table.alias}` : "";
|
|
20687
21012
|
const joinType = join2.type === "INNER" ? "JOIN" : `${join2.type} JOIN`;
|
|
20688
21013
|
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
@@ -20696,10 +21021,10 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20696
21021
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|
|
20697
21022
|
}
|
|
20698
21023
|
}
|
|
20699
|
-
lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
|
|
21024
|
+
lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans));
|
|
20700
21025
|
return lines;
|
|
20701
21026
|
}
|
|
20702
|
-
function buildUnionPlan(stmt, capabilities, orderPlans) {
|
|
21027
|
+
function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
|
|
20703
21028
|
const selects = [];
|
|
20704
21029
|
const collect = (u) => {
|
|
20705
21030
|
if (u.type === "SELECT") {
|
|
@@ -20713,25 +21038,34 @@ function buildUnionPlan(stmt, capabilities, orderPlans) {
|
|
|
20713
21038
|
const lines = [];
|
|
20714
21039
|
selects.forEach((sel, i) => {
|
|
20715
21040
|
if (i > 0) lines.push("");
|
|
20716
|
-
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
|
|
21041
|
+
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20717
21042
|
});
|
|
20718
21043
|
return lines;
|
|
20719
21044
|
}
|
|
20720
|
-
function buildWithPlan(stmt, capabilities, orderPlans) {
|
|
21045
|
+
function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
|
|
20721
21046
|
const lines = [];
|
|
20722
21047
|
for (const cte of stmt.ctes) {
|
|
20723
21048
|
if (cte.query.type === "SELECT") {
|
|
20724
|
-
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
|
|
21049
|
+
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20725
21050
|
lines.push("");
|
|
20726
21051
|
}
|
|
20727
21052
|
}
|
|
20728
21053
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
20729
|
-
lines.push(...buildExplainPlan(
|
|
21054
|
+
lines.push(...buildExplainPlan(
|
|
21055
|
+
stmt.query,
|
|
21056
|
+
"[main]",
|
|
21057
|
+
capabilities,
|
|
21058
|
+
orderPlans,
|
|
21059
|
+
100,
|
|
21060
|
+
DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
|
|
21061
|
+
1e4,
|
|
21062
|
+
plainGroupByPlans
|
|
21063
|
+
));
|
|
20730
21064
|
}
|
|
20731
21065
|
if (canInlineSingleCte(stmt)) {
|
|
20732
21066
|
lines.push("");
|
|
20733
21067
|
const inlined = buildInlinedQuery(stmt);
|
|
20734
|
-
lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
|
|
21068
|
+
lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans, plainGroupByPlans));
|
|
20735
21069
|
}
|
|
20736
21070
|
return lines;
|
|
20737
21071
|
}
|
|
@@ -20764,7 +21098,7 @@ function collectFullScanReasons(stmt) {
|
|
|
20764
21098
|
r.push("ORDER BY \u306B\u5F0F");
|
|
20765
21099
|
return r;
|
|
20766
21100
|
}
|
|
20767
|
-
function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
21101
|
+
function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans) {
|
|
20768
21102
|
const lines = [];
|
|
20769
21103
|
let idx = 1;
|
|
20770
21104
|
const visitWhere = (w) => {
|
|
@@ -20773,16 +21107,16 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
|
20773
21107
|
case "BINARY":
|
|
20774
21108
|
if (w.right.type === "SCALAR_SUBQUERY") {
|
|
20775
21109
|
lines.push("");
|
|
20776
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21110
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20777
21111
|
}
|
|
20778
21112
|
if (w.right.type === "SUBQUERY_IN_LIST") {
|
|
20779
21113
|
lines.push("");
|
|
20780
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21114
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20781
21115
|
}
|
|
20782
21116
|
break;
|
|
20783
21117
|
case "EXISTS":
|
|
20784
21118
|
lines.push("");
|
|
20785
|
-
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21119
|
+
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20786
21120
|
break;
|
|
20787
21121
|
case "LOGICAL":
|
|
20788
21122
|
visitWhere(w.left);
|
|
@@ -20802,7 +21136,7 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
|
20802
21136
|
for (const col of stmt.columns) {
|
|
20803
21137
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
20804
21138
|
lines.push("");
|
|
20805
|
-
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21139
|
+
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20806
21140
|
}
|
|
20807
21141
|
}
|
|
20808
21142
|
if (stmt.having) visitWhere(stmt.having);
|
|
@@ -20820,7 +21154,7 @@ function buildInsertPlan(stmt, label, dmlMaxRows = DEFAULT_APPLY_MAX_ROWS, dmlMa
|
|
|
20820
21154
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
20821
21155
|
return stmt.applyBlocks?.length ? [...lines, ...formatStaticApplyDiagnostic(buildStaticApplyDiagnostic(stmt, dmlMaxRows, dmlMaxSubtableRows))] : lines;
|
|
20822
21156
|
}
|
|
20823
|
-
function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
21157
|
+
function buildInsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans) {
|
|
20824
21158
|
const lines = [];
|
|
20825
21159
|
if (label) lines.push(label);
|
|
20826
21160
|
lines.push(` [INSERT SELECT]`);
|
|
@@ -20828,7 +21162,7 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20828
21162
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
20829
21163
|
lines.push(` api: POST /k/v1/records.json\uFF08\u4EF6\u6570\u306F SELECT \u7D50\u679C\u306B\u4F9D\u5B58\u3001100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`);
|
|
20830
21164
|
lines.push("");
|
|
20831
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
21165
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans));
|
|
20832
21166
|
return lines;
|
|
20833
21167
|
}
|
|
20834
21168
|
function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
|
|
@@ -20982,7 +21316,7 @@ function formatStaticApplyDiagnostic(diagnostic2) {
|
|
|
20982
21316
|
lines.push("records API: 0", "mutation API: 0");
|
|
20983
21317
|
return lines;
|
|
20984
21318
|
}
|
|
20985
|
-
function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
21319
|
+
function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans) {
|
|
20986
21320
|
const lines = [
|
|
20987
21321
|
...label ? [label] : [],
|
|
20988
21322
|
` [UPSERT SELECT]`,
|
|
@@ -20992,7 +21326,7 @@ function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20992
21326
|
` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json\uFF08100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`,
|
|
20993
21327
|
``
|
|
20994
21328
|
];
|
|
20995
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
21329
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans));
|
|
20996
21330
|
return lines;
|
|
20997
21331
|
}
|
|
20998
21332
|
function buildReorderPlan(stmt, label) {
|