@rex0220/kintone-sql-tools 3.22.0 → 3.24.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 +650 -102
- 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 +81 -15
- package/dist-engine/meta/esm.json +81 -15
- package/dist-engine/meta/umd.json +81 -15
- package/dist-mcp/ksql-mcp.js +659 -104
- 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
|
);
|
|
@@ -12408,6 +12604,133 @@ function requireExactRelativeDatePushdown(result) {
|
|
|
12408
12604
|
};
|
|
12409
12605
|
}
|
|
12410
12606
|
|
|
12607
|
+
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
12608
|
+
function buildRelativeDateFullScanExactPlan(input) {
|
|
12609
|
+
const {
|
|
12610
|
+
select,
|
|
12611
|
+
selectMode,
|
|
12612
|
+
capability,
|
|
12613
|
+
context,
|
|
12614
|
+
serializedWholeWhere,
|
|
12615
|
+
relativeFunctionNames
|
|
12616
|
+
} = input;
|
|
12617
|
+
if (select.where === null) return null;
|
|
12618
|
+
if (select.from.appId <= 0 || select.from.cteName !== null) return null;
|
|
12619
|
+
if (select.from.subtableCode) return null;
|
|
12620
|
+
if (select.joins.length > 0) return null;
|
|
12621
|
+
if (!context.allowFullScanExact) return null;
|
|
12622
|
+
if (select.orderMode === "KINTONE_NATIVE") return null;
|
|
12623
|
+
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
12624
|
+
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
12625
|
+
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
12626
|
+
const occurrences = relativeDateFunctionOccurrencesInWhere(select.where);
|
|
12627
|
+
if (occurrences.length === 0) return null;
|
|
12628
|
+
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
12629
|
+
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
12630
|
+
return null;
|
|
12631
|
+
}
|
|
12632
|
+
const prefilterPlan = {
|
|
12633
|
+
prefilterWhere: select.where,
|
|
12634
|
+
residualWhere: null,
|
|
12635
|
+
exactRelativeLeaves: collectExactRelativeLeaves(select.where),
|
|
12636
|
+
relativeFunctionNames: new Set(occurrences),
|
|
12637
|
+
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
12638
|
+
capability: capability.capability,
|
|
12639
|
+
reasons: capability.reasons
|
|
12640
|
+
};
|
|
12641
|
+
const plan = {
|
|
12642
|
+
allowForm: "FULL_SCAN_EXACT",
|
|
12643
|
+
clientWhereEvaluation: false,
|
|
12644
|
+
serializedWholeWhere,
|
|
12645
|
+
prefilterPlan
|
|
12646
|
+
};
|
|
12647
|
+
assertRelativeDateFullScanExactPlan(plan, input, occurrences);
|
|
12648
|
+
return plan;
|
|
12649
|
+
}
|
|
12650
|
+
function assertRelativeDateFullScanExactPlan(plan, input, occurrences) {
|
|
12651
|
+
if (plan.allowForm !== "FULL_SCAN_EXACT") {
|
|
12652
|
+
throw new Error("FULL_SCAN_EXACT invariant: allowForm");
|
|
12653
|
+
}
|
|
12654
|
+
if (plan.clientWhereEvaluation !== false) {
|
|
12655
|
+
throw new Error("FULL_SCAN_EXACT invariant: clientWhereEvaluation");
|
|
12656
|
+
}
|
|
12657
|
+
if (input.capability.capability !== "EXACT_PUSHDOWN") {
|
|
12658
|
+
throw new Error("FULL_SCAN_EXACT invariant: capability");
|
|
12659
|
+
}
|
|
12660
|
+
if (input.select.where === null || plan.prefilterPlan.prefilterWhere !== input.select.where) {
|
|
12661
|
+
throw new Error("FULL_SCAN_EXACT invariant: whole WHERE identity");
|
|
12662
|
+
}
|
|
12663
|
+
if (plan.prefilterPlan.residualWhere !== null) {
|
|
12664
|
+
throw new Error("FULL_SCAN_EXACT invariant: residualWhere");
|
|
12665
|
+
}
|
|
12666
|
+
if (plan.prefilterPlan.capability !== "EXACT_PUSHDOWN") {
|
|
12667
|
+
throw new Error("FULL_SCAN_EXACT invariant: transport capability");
|
|
12668
|
+
}
|
|
12669
|
+
if (!serializedMultisetContains(plan.serializedWholeWhere, occurrences)) {
|
|
12670
|
+
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
12671
|
+
}
|
|
12672
|
+
}
|
|
12673
|
+
function relativeDateFunctionOccurrencesInWhere(where) {
|
|
12674
|
+
const names = [];
|
|
12675
|
+
const visit = (node) => {
|
|
12676
|
+
if (Array.isArray(node)) {
|
|
12677
|
+
node.forEach(visit);
|
|
12678
|
+
return;
|
|
12679
|
+
}
|
|
12680
|
+
if (node === null || typeof node !== "object") return;
|
|
12681
|
+
const value = node;
|
|
12682
|
+
if (value["type"] === "SELECT") return;
|
|
12683
|
+
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isRelativeDateFunctionName(value["name"])) {
|
|
12684
|
+
names.push(value["name"]);
|
|
12685
|
+
return;
|
|
12686
|
+
}
|
|
12687
|
+
Object.values(value).forEach(visit);
|
|
12688
|
+
};
|
|
12689
|
+
visit(where);
|
|
12690
|
+
return names;
|
|
12691
|
+
}
|
|
12692
|
+
function collectExactRelativeLeaves(where) {
|
|
12693
|
+
const leaves = [];
|
|
12694
|
+
const visit = (node) => {
|
|
12695
|
+
switch (node.type) {
|
|
12696
|
+
case "BINARY":
|
|
12697
|
+
if (node.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(node.right.name)) {
|
|
12698
|
+
leaves.push(node);
|
|
12699
|
+
}
|
|
12700
|
+
return;
|
|
12701
|
+
case "LOGICAL":
|
|
12702
|
+
visit(node.left);
|
|
12703
|
+
visit(node.right);
|
|
12704
|
+
return;
|
|
12705
|
+
case "NOT":
|
|
12706
|
+
case "GROUP":
|
|
12707
|
+
visit(node.expr);
|
|
12708
|
+
return;
|
|
12709
|
+
case "EXISTS":
|
|
12710
|
+
case "NULL_CHECK":
|
|
12711
|
+
case "BOOLEAN":
|
|
12712
|
+
return;
|
|
12713
|
+
}
|
|
12714
|
+
};
|
|
12715
|
+
visit(where);
|
|
12716
|
+
return leaves;
|
|
12717
|
+
}
|
|
12718
|
+
function sameOccurrenceList(actual, expected) {
|
|
12719
|
+
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
12720
|
+
}
|
|
12721
|
+
function serializedMultisetContains(query, expectedNames) {
|
|
12722
|
+
const expected = /* @__PURE__ */ new Map();
|
|
12723
|
+
for (const name of expectedNames) {
|
|
12724
|
+
if (!isRelativeDateFunctionName(name)) return false;
|
|
12725
|
+
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
12726
|
+
}
|
|
12727
|
+
for (const [name, count] of expected) {
|
|
12728
|
+
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
12729
|
+
if ((matches?.length ?? 0) < count) return false;
|
|
12730
|
+
}
|
|
12731
|
+
return true;
|
|
12732
|
+
}
|
|
12733
|
+
|
|
12411
12734
|
// src/core/optimization/relativeDatePushdownGuard.ts
|
|
12412
12735
|
function relativeDateFunctionNamesInNode(node, stopAtNestedSelect) {
|
|
12413
12736
|
const names = [];
|
|
@@ -12453,7 +12776,7 @@ function nestedSelects(node, root) {
|
|
|
12453
12776
|
visit(node);
|
|
12454
12777
|
return found;
|
|
12455
12778
|
}
|
|
12456
|
-
function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = true) {
|
|
12779
|
+
function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = true, allowFullScanExact = true) {
|
|
12457
12780
|
const functionNames = relativeDateFunctionNamesInWhere(select.where);
|
|
12458
12781
|
if (functionNames.length > 0) {
|
|
12459
12782
|
candidates.push({
|
|
@@ -12462,7 +12785,9 @@ function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = t
|
|
|
12462
12785
|
where: select.where,
|
|
12463
12786
|
functionNames,
|
|
12464
12787
|
path,
|
|
12465
|
-
allowPhase2Prefilter: allowPhase2
|
|
12788
|
+
allowPhase2Prefilter: allowPhase2,
|
|
12789
|
+
allowFullScanExact,
|
|
12790
|
+
relativeFunctionOccurrences: relativeDateFunctionOccurrencesInWhere(select.where)
|
|
12466
12791
|
});
|
|
12467
12792
|
}
|
|
12468
12793
|
nestedSelects(select, select).forEach(
|
|
@@ -12471,31 +12796,35 @@ function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = t
|
|
|
12471
12796
|
`${path}.select-source[${index}]`,
|
|
12472
12797
|
candidates,
|
|
12473
12798
|
forceForbidden,
|
|
12474
|
-
allowPhase2
|
|
12799
|
+
allowPhase2,
|
|
12800
|
+
allowFullScanExact
|
|
12475
12801
|
)
|
|
12476
12802
|
);
|
|
12477
12803
|
}
|
|
12478
|
-
function collectUnion(union, path, candidates, forceForbidden) {
|
|
12479
|
-
if (union.left.type === "UNION")
|
|
12480
|
-
|
|
12481
|
-
|
|
12804
|
+
function collectUnion(union, path, candidates, forceForbidden, allowFullScanExact = true) {
|
|
12805
|
+
if (union.left.type === "UNION") {
|
|
12806
|
+
collectUnion(union.left, `${path}.left`, candidates, forceForbidden, allowFullScanExact);
|
|
12807
|
+
} else {
|
|
12808
|
+
collectSelect(union.left, `${path}.left`, candidates, forceForbidden, true, allowFullScanExact);
|
|
12809
|
+
}
|
|
12810
|
+
collectSelect(union.right, `${path}.right`, candidates, forceForbidden, true, allowFullScanExact);
|
|
12482
12811
|
}
|
|
12483
12812
|
function collectWith(statement, path, candidates, inheritedForbidden) {
|
|
12484
12813
|
if (!inheritedForbidden && canInlineSingleCte(statement)) {
|
|
12485
|
-
collectSelect(buildInlinedQuery(statement), `${path}.inlined`, candidates, false);
|
|
12814
|
+
collectSelect(buildInlinedQuery(statement), `${path}.inlined`, candidates, false, true, false);
|
|
12486
12815
|
return;
|
|
12487
12816
|
}
|
|
12488
12817
|
statement.ctes.forEach((cte, index) => {
|
|
12489
12818
|
if (cte.query.type === "SELECT") {
|
|
12490
|
-
collectSelect(cte.query, `${path}.cte[${index}]`, candidates, true);
|
|
12819
|
+
collectSelect(cte.query, `${path}.cte[${index}]`, candidates, true, true, false);
|
|
12491
12820
|
} else if (cte.query.type === "UNION") {
|
|
12492
|
-
collectUnion(cte.query, `${path}.cte[${index}]`, candidates, true);
|
|
12821
|
+
collectUnion(cte.query, `${path}.cte[${index}]`, candidates, true, false);
|
|
12493
12822
|
}
|
|
12494
12823
|
});
|
|
12495
12824
|
if (statement.query.type === "SELECT") {
|
|
12496
|
-
collectSelect(statement.query, `${path}.main`, candidates, true);
|
|
12825
|
+
collectSelect(statement.query, `${path}.main`, candidates, true, true, false);
|
|
12497
12826
|
} else {
|
|
12498
|
-
collectUnion(statement.query, `${path}.main`, candidates, true);
|
|
12827
|
+
collectUnion(statement.query, `${path}.main`, candidates, true, false);
|
|
12499
12828
|
}
|
|
12500
12829
|
}
|
|
12501
12830
|
function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
@@ -12511,8 +12840,8 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12511
12840
|
return;
|
|
12512
12841
|
case "CREATE_TEMP_TABLE":
|
|
12513
12842
|
if (statement.query.type === "WITH") collectWith(statement.query, `${path}.query`, candidates, true);
|
|
12514
|
-
else if (statement.query.type === "UNION") collectUnion(statement.query, `${path}.query`, candidates, true);
|
|
12515
|
-
else collectSelect(statement.query, `${path}.query`, candidates, true);
|
|
12843
|
+
else if (statement.query.type === "UNION") collectUnion(statement.query, `${path}.query`, candidates, true, false);
|
|
12844
|
+
else collectSelect(statement.query, `${path}.query`, candidates, true, true, false);
|
|
12516
12845
|
return;
|
|
12517
12846
|
case "EXPLAIN":
|
|
12518
12847
|
collectStatement(statement.query, `${path}.query`, candidates, forceForbidden);
|
|
@@ -12526,11 +12855,13 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12526
12855
|
source: statement,
|
|
12527
12856
|
where: statement.where,
|
|
12528
12857
|
functionNames,
|
|
12529
|
-
path
|
|
12858
|
+
path,
|
|
12859
|
+
allowFullScanExact: false,
|
|
12860
|
+
relativeFunctionOccurrences: relativeDateFunctionOccurrencesInWhere(statement.where)
|
|
12530
12861
|
});
|
|
12531
12862
|
}
|
|
12532
12863
|
nestedSelects(statement, statement).forEach(
|
|
12533
|
-
(select, index) => collectSelect(select, `${path}.select-source[${index}]`, candidates, true)
|
|
12864
|
+
(select, index) => collectSelect(select, `${path}.select-source[${index}]`, candidates, true, true, false)
|
|
12534
12865
|
);
|
|
12535
12866
|
return;
|
|
12536
12867
|
}
|
|
@@ -12544,7 +12875,9 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12544
12875
|
source: statement,
|
|
12545
12876
|
where: statement.where,
|
|
12546
12877
|
functionNames,
|
|
12547
|
-
path
|
|
12878
|
+
path,
|
|
12879
|
+
allowFullScanExact: false,
|
|
12880
|
+
relativeFunctionOccurrences: relativeDateFunctionOccurrencesInWhere(statement.where)
|
|
12548
12881
|
});
|
|
12549
12882
|
}
|
|
12550
12883
|
if (statement.type === "UPDATE" && statement.applyBlocks?.length) {
|
|
@@ -12555,7 +12888,9 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12555
12888
|
source: statement,
|
|
12556
12889
|
where: null,
|
|
12557
12890
|
functionNames: applyFunctions,
|
|
12558
|
-
path: `${path}.apply
|
|
12891
|
+
path: `${path}.apply`,
|
|
12892
|
+
allowFullScanExact: false,
|
|
12893
|
+
relativeFunctionOccurrences: []
|
|
12559
12894
|
});
|
|
12560
12895
|
}
|
|
12561
12896
|
}
|
|
@@ -12565,6 +12900,7 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12565
12900
|
`${path}.select-source[${index}]`,
|
|
12566
12901
|
candidates,
|
|
12567
12902
|
forceForbidden,
|
|
12903
|
+
false,
|
|
12568
12904
|
false
|
|
12569
12905
|
)
|
|
12570
12906
|
);
|
|
@@ -12577,6 +12913,7 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12577
12913
|
`${path}.select-source[${index}]`,
|
|
12578
12914
|
candidates,
|
|
12579
12915
|
forceForbidden,
|
|
12916
|
+
false,
|
|
12580
12917
|
false
|
|
12581
12918
|
)
|
|
12582
12919
|
);
|
|
@@ -12655,6 +12992,21 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12655
12992
|
allowed2 = true;
|
|
12656
12993
|
}
|
|
12657
12994
|
}
|
|
12995
|
+
let fullScanExactPlan;
|
|
12996
|
+
if (!allowed2 && capability2.capability === "EXACT_PUSHDOWN") {
|
|
12997
|
+
fullScanExactPlan = buildRelativeDateFullScanExactPlan({
|
|
12998
|
+
select,
|
|
12999
|
+
selectMode,
|
|
13000
|
+
capability: capability2,
|
|
13001
|
+
context: { allowFullScanExact: candidate.allowFullScanExact },
|
|
13002
|
+
serializedWholeWhere: restQuery2 || null,
|
|
13003
|
+
relativeFunctionNames: candidate.relativeFunctionOccurrences
|
|
13004
|
+
}) ?? void 0;
|
|
13005
|
+
if (fullScanExactPlan) {
|
|
13006
|
+
prefilterPlan = fullScanExactPlan.prefilterPlan;
|
|
13007
|
+
allowed2 = true;
|
|
13008
|
+
}
|
|
13009
|
+
}
|
|
12658
13010
|
const node2 = {
|
|
12659
13011
|
kind: candidate.kind,
|
|
12660
13012
|
source: candidate.source,
|
|
@@ -12664,6 +13016,7 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12664
13016
|
capability: capability2,
|
|
12665
13017
|
restQuery: restQuery2,
|
|
12666
13018
|
...prefilterPlan ? { prefilterPlan, phase2PrefilterEligible } : {},
|
|
13019
|
+
...fullScanExactPlan ? { fullScanExactPlan, allowForm: fullScanExactPlan.allowForm } : {},
|
|
12667
13020
|
clientWhereEvaluation: !allowed2,
|
|
12668
13021
|
allowed: allowed2
|
|
12669
13022
|
};
|
|
@@ -12795,7 +13148,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
12795
13148
|
return reject(["PREFILTER_SERIALIZATION_FAILED"]);
|
|
12796
13149
|
}
|
|
12797
13150
|
const expectedNames = confirmedLeaves.map((leaf) => relativeNameOf(leaf));
|
|
12798
|
-
if (!containsFunctions(prefilterQuery, [...new Set(expectedNames)]) || !
|
|
13151
|
+
if (!containsFunctions(prefilterQuery, [...new Set(expectedNames)]) || !serializedMultisetContains2(prefilterQuery, expectedNames)) {
|
|
12799
13152
|
return reject(["PREFILTER_FUNCTION_MISSING"]);
|
|
12800
13153
|
}
|
|
12801
13154
|
const residualWhere = testSeam.rewriteResidual ? testSeam.rewriteResidual(stmt.where, adoptedLeaves) : replaceAdoptedLeaves(stmt.where, adoptedLeaves);
|
|
@@ -13017,7 +13370,7 @@ function collectFieldMetadata(where, resolveField2) {
|
|
|
13017
13370
|
}
|
|
13018
13371
|
return { fieldTypes, fieldOptions };
|
|
13019
13372
|
}
|
|
13020
|
-
function
|
|
13373
|
+
function serializedMultisetContains2(query, expectedNames) {
|
|
13021
13374
|
const expected = /* @__PURE__ */ new Map();
|
|
13022
13375
|
for (const name of expectedNames) {
|
|
13023
13376
|
if (!RELATIVE_DATE_FUNCTION_NAMES.has(name)) return false;
|
|
@@ -15229,12 +15582,20 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15229
15582
|
}
|
|
15230
15583
|
return result;
|
|
15231
15584
|
}
|
|
15585
|
+
const plainGroupByPlan = await buildRuntimePlainGroupByPlan(
|
|
15586
|
+
stmt,
|
|
15587
|
+
client,
|
|
15588
|
+
cacheContext,
|
|
15589
|
+
cteCache
|
|
15590
|
+
);
|
|
15232
15591
|
await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
|
|
15233
15592
|
const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
|
|
15234
15593
|
if (whereCapability.capability === "UNSUPPORTED") {
|
|
15235
15594
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
15236
15595
|
}
|
|
15596
|
+
const staticMode = resolveSelectMode(stmt);
|
|
15237
15597
|
let prefilterPlan;
|
|
15598
|
+
let fullScanExactPlan;
|
|
15238
15599
|
if (whereCapability.capability === "SUPERSET_PREFILTER") {
|
|
15239
15600
|
const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, cteCache);
|
|
15240
15601
|
const decomposition = decomposeRelativeDatePrefilter(stmt, resolver);
|
|
@@ -15242,7 +15603,23 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15242
15603
|
prefilterPlan = decomposition.plan;
|
|
15243
15604
|
}
|
|
15244
15605
|
}
|
|
15245
|
-
|
|
15606
|
+
if (prefilterPlan === void 0 && whereCapability.capability === "EXACT_PUSHDOWN" && stmt.where !== null) {
|
|
15607
|
+
let serializedWholeWhere = null;
|
|
15608
|
+
try {
|
|
15609
|
+
serializedWholeWhere = whereToKintone(stmt.where);
|
|
15610
|
+
} catch {
|
|
15611
|
+
serializedWholeWhere = null;
|
|
15612
|
+
}
|
|
15613
|
+
fullScanExactPlan = buildRelativeDateFullScanExactPlan({
|
|
15614
|
+
select: stmt,
|
|
15615
|
+
selectMode: staticMode,
|
|
15616
|
+
capability: whereCapability,
|
|
15617
|
+
context: { allowFullScanExact: true },
|
|
15618
|
+
serializedWholeWhere,
|
|
15619
|
+
relativeFunctionNames: relativeDateFunctionOccurrencesInWhere(stmt.where)
|
|
15620
|
+
}) ?? void 0;
|
|
15621
|
+
if (fullScanExactPlan) prefilterPlan = fullScanExactPlan.prefilterPlan;
|
|
15622
|
+
}
|
|
15246
15623
|
const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
|
|
15247
15624
|
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
15248
15625
|
const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
@@ -15260,20 +15637,34 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15260
15637
|
client,
|
|
15261
15638
|
cacheContext
|
|
15262
15639
|
);
|
|
15263
|
-
const completePolicy = buildCompleteInputPolicy(
|
|
15640
|
+
const completePolicy = buildCompleteInputPolicy(
|
|
15641
|
+
stmt,
|
|
15642
|
+
options,
|
|
15643
|
+
orderPlan
|
|
15644
|
+
);
|
|
15645
|
+
const failClosedForB72Local = fullScanExactPlan !== void 0 && (staticMode === "FULL_SCAN" || orderPlan?.kind === "CANONICAL_LOCAL");
|
|
15646
|
+
const executionClient = failClosedForB72Local ? wrapClientWithSearchAbort(client, { aborted: false }, true) : client;
|
|
15264
15647
|
try {
|
|
15265
15648
|
if (mode === "SIMPLE") {
|
|
15266
|
-
result = await executeSimpleSelect(
|
|
15649
|
+
result = await executeSimpleSelect(
|
|
15650
|
+
stmt,
|
|
15651
|
+
executionClient,
|
|
15652
|
+
completePolicy.effectiveOptions,
|
|
15653
|
+
cacheContext,
|
|
15654
|
+
orderPlan,
|
|
15655
|
+
orderMeta
|
|
15656
|
+
);
|
|
15267
15657
|
} else {
|
|
15268
15658
|
result = await executeFullScanSelect(
|
|
15269
15659
|
stmt,
|
|
15270
|
-
|
|
15660
|
+
executionClient,
|
|
15271
15661
|
completePolicy.effectiveOptions,
|
|
15272
15662
|
cacheContext,
|
|
15273
15663
|
cteCache,
|
|
15274
|
-
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
15664
|
+
whereCapability.capability === "EXACT_PUSHDOWN" && prefilterPlan === void 0,
|
|
15275
15665
|
orderMeta,
|
|
15276
|
-
prefilterPlan
|
|
15666
|
+
prefilterPlan,
|
|
15667
|
+
plainGroupByPlan
|
|
15277
15668
|
);
|
|
15278
15669
|
}
|
|
15279
15670
|
} catch (error) {
|
|
@@ -15287,6 +15678,78 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15287
15678
|
}
|
|
15288
15679
|
return result;
|
|
15289
15680
|
}
|
|
15681
|
+
async function buildRuntimePlainGroupByPlan(stmt, client, cacheContext, materializedTables) {
|
|
15682
|
+
if (stmt.groupBy.length === 0) return void 0;
|
|
15683
|
+
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
15684
|
+
const inputs = await Promise.all(sources.map(async (source) => {
|
|
15685
|
+
if (source.cteName !== null) {
|
|
15686
|
+
const materialized = materializedTables?.get(source.cteName);
|
|
15687
|
+
if (!materialized) {
|
|
15688
|
+
throw new Error(`InternalError: materialized schema ${source.cteName} is not available for GROUP BY planning.`);
|
|
15689
|
+
}
|
|
15690
|
+
return { kind: "MATERIALIZED", columns: materialized.columns };
|
|
15691
|
+
}
|
|
15692
|
+
const fields = await getFieldsCached(source.appId, client, cacheContext);
|
|
15693
|
+
if (!source.subtableCode) {
|
|
15694
|
+
return {
|
|
15695
|
+
kind: "APP",
|
|
15696
|
+
fieldCodes: fields.filter((field) => !field.inSubtable).map((field) => field.code)
|
|
15697
|
+
};
|
|
15698
|
+
}
|
|
15699
|
+
const exactChildren = fields.filter(
|
|
15700
|
+
(field) => field.inSubtable && field.subtableCode === source.subtableCode
|
|
15701
|
+
);
|
|
15702
|
+
const childFields = exactChildren.length > 0 ? exactChildren : fields.filter((field) => field.inSubtable && field.subtableCode === void 0);
|
|
15703
|
+
return {
|
|
15704
|
+
kind: "SUBTABLE",
|
|
15705
|
+
childFieldCodes: childFields.map((field) => field.code),
|
|
15706
|
+
parentFieldCodes: fields.filter((field) => !field.inSubtable).map((field) => field.code)
|
|
15707
|
+
};
|
|
15708
|
+
}));
|
|
15709
|
+
const schemas = resolvePlainGroupBySourceSchemas(
|
|
15710
|
+
stmt,
|
|
15711
|
+
(_source, sourceIndex) => inputs[sourceIndex]
|
|
15712
|
+
);
|
|
15713
|
+
const groupBy = stmt.groupBy;
|
|
15714
|
+
const plan = planPlainGroupByResolution(groupBy, stmt.columns, schemas);
|
|
15715
|
+
assertRuntimePlainGroupByPlan(stmt, groupBy, plan);
|
|
15716
|
+
return plan;
|
|
15717
|
+
}
|
|
15718
|
+
function assertRuntimePlainGroupByPlan(stmt, groupBy, plan) {
|
|
15719
|
+
const physicalAppIds = [
|
|
15720
|
+
stmt.from,
|
|
15721
|
+
...stmt.joins.map((join2) => join2.table)
|
|
15722
|
+
].flatMap((source) => source.cteName === null ? [source.appId] : []);
|
|
15723
|
+
const uniquePhysicalAppIds = [...new Set(physicalAppIds)];
|
|
15724
|
+
const primaryPhysicalAppId = uniquePhysicalAppIds.length === 1 ? uniquePhysicalAppIds[0] : stmt.from.cteName === null ? stmt.from.appId : null;
|
|
15725
|
+
plan.items.forEach((item, index) => {
|
|
15726
|
+
if (item.kind === "EXPRESSION" || item.kind === "PHYSICAL" || item.kind === "ALIAS_SAFE") return;
|
|
15727
|
+
const key = groupBy[index];
|
|
15728
|
+
const name = key?.type === "FIELD_NAME" ? key.name : "(expression)";
|
|
15729
|
+
if (item.kind === "UNKNOWN") {
|
|
15730
|
+
const appSuffix = primaryPhysicalAppId === null ? "" : ` (APP${primaryPhysicalAppId})`;
|
|
15731
|
+
throw new Error(`ArgumentError: unknown field code(s): ${item.name}${appSuffix}`);
|
|
15732
|
+
}
|
|
15733
|
+
if (item.kind === "ALIAS_REJECT") {
|
|
15734
|
+
if (item.reason === "DUPLICATE") {
|
|
15735
|
+
throw new Error(
|
|
15736
|
+
`ArgumentError: GROUP BY alias ${name} is ambiguous across multiple SELECT columns (reason=GROUP_BY_ALIAS_AMBIGUOUS).`
|
|
15737
|
+
);
|
|
15738
|
+
}
|
|
15739
|
+
if (item.reason === "POST_GROUP_ONLY") {
|
|
15740
|
+
throw new Error(
|
|
15741
|
+
`ArgumentError: GROUP BY alias ${name} requires post-group evaluation (reason=GROUP_BY_ALIAS_POST_GROUP_ONLY).`
|
|
15742
|
+
);
|
|
15743
|
+
}
|
|
15744
|
+
throw new Error(
|
|
15745
|
+
`ArgumentError: GROUP BY alias ${name} depends on aggregate evaluation (reason=GROUP_BY_ALIAS_AGGREGATE).`
|
|
15746
|
+
);
|
|
15747
|
+
}
|
|
15748
|
+
throw new Error(
|
|
15749
|
+
`InternalError: deferred GROUP BY field ${item.name} reached runtime planning.`
|
|
15750
|
+
);
|
|
15751
|
+
});
|
|
15752
|
+
}
|
|
15290
15753
|
var resolvedGroupingSpecs = /* @__PURE__ */ new WeakMap();
|
|
15291
15754
|
async function validateStatementGroupingPlanning(statement, client, cacheContext) {
|
|
15292
15755
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -16218,7 +16681,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
16218
16681
|
};
|
|
16219
16682
|
return { row, having };
|
|
16220
16683
|
}
|
|
16221
|
-
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta, prefilterPlan) {
|
|
16684
|
+
async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta, prefilterPlan, plainGroupByPlan) {
|
|
16222
16685
|
const maxRecords = options.maxRecords ?? 1e4;
|
|
16223
16686
|
const warnings = /* @__PURE__ */ new Set();
|
|
16224
16687
|
const parallel = options.fetchParallel ?? 1;
|
|
@@ -16248,6 +16711,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16248
16711
|
throw new Error("internal error: relative-date prefilter must disable original WHERE pushdown.");
|
|
16249
16712
|
}
|
|
16250
16713
|
const mainFetchCondition = prefilterPlan ? prefilterPlan.prefilterWhere : mainPushDown;
|
|
16714
|
+
const scalarCache = await resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
16251
16715
|
const constantFalse = isConstantFalseWhere(stmt.where);
|
|
16252
16716
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
16253
16717
|
stmt,
|
|
@@ -16259,7 +16723,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16259
16723
|
options.onLimitReached ?? "error",
|
|
16260
16724
|
warnings,
|
|
16261
16725
|
mainFetchCondition,
|
|
16262
|
-
allowOriginalWherePushdown
|
|
16726
|
+
allowOriginalWherePushdown,
|
|
16727
|
+
plainGroupByPlan
|
|
16263
16728
|
);
|
|
16264
16729
|
const parallelJoins = [];
|
|
16265
16730
|
const onOptJoins = [];
|
|
@@ -16281,17 +16746,16 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16281
16746
|
false,
|
|
16282
16747
|
options.onLimitReached ?? "error",
|
|
16283
16748
|
warnings,
|
|
16284
|
-
jCond
|
|
16749
|
+
jCond,
|
|
16750
|
+
true,
|
|
16751
|
+
plainGroupByPlan
|
|
16285
16752
|
)
|
|
16286
16753
|
});
|
|
16287
16754
|
} else {
|
|
16288
16755
|
onOptJoins.push(join2);
|
|
16289
16756
|
}
|
|
16290
16757
|
}
|
|
16291
|
-
const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
|
|
16292
16758
|
const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
16293
|
-
scalarCachePromise.catch(() => {
|
|
16294
|
-
});
|
|
16295
16759
|
orderByMetaPromise.catch(() => {
|
|
16296
16760
|
});
|
|
16297
16761
|
const mainRecords = await mainFetch;
|
|
@@ -16310,7 +16774,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16310
16774
|
parallel,
|
|
16311
16775
|
options.onLimitReached ?? "error",
|
|
16312
16776
|
warnings,
|
|
16313
|
-
null
|
|
16777
|
+
null,
|
|
16778
|
+
plainGroupByPlan
|
|
16314
16779
|
);
|
|
16315
16780
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
16316
16781
|
stmt,
|
|
@@ -16321,11 +16786,12 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16321
16786
|
false,
|
|
16322
16787
|
options.onLimitReached ?? "error",
|
|
16323
16788
|
warnings,
|
|
16324
|
-
null
|
|
16789
|
+
null,
|
|
16790
|
+
true,
|
|
16791
|
+
plainGroupByPlan
|
|
16325
16792
|
);
|
|
16326
16793
|
tables.set(join2.table.alias, joinRecords);
|
|
16327
16794
|
}));
|
|
16328
|
-
const scalarCache = await scalarCachePromise;
|
|
16329
16795
|
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
16330
16796
|
const { rows, columns } = runFullScan({
|
|
16331
16797
|
tables,
|
|
@@ -16341,7 +16807,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16341
16807
|
aggregateSortKindResolver,
|
|
16342
16808
|
appliedKlikes: prefilterPlan?.appliedKlikes ?? pushdownPlan.appliedKlikes,
|
|
16343
16809
|
...prefilterPlan ? { residualWhere: prefilterPlan.residualWhere } : {},
|
|
16344
|
-
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt)
|
|
16810
|
+
resolvedGroupingSpec: resolvedGroupingSpecs.get(stmt),
|
|
16811
|
+
plainGroupByPlan
|
|
16345
16812
|
});
|
|
16346
16813
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
16347
16814
|
}
|
|
@@ -16434,6 +16901,12 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
|
|
|
16434
16901
|
async function executeFullScanWithCte(stmt, client, options, cteCache, cacheContext) {
|
|
16435
16902
|
await validateSelectGroupingPlanning(stmt, client, cacheContext, cteCache);
|
|
16436
16903
|
const resolvedGroupingSpec = resolvedGroupingSpecs.get(stmt);
|
|
16904
|
+
const plainGroupByPlan = await buildRuntimePlainGroupByPlan(
|
|
16905
|
+
stmt,
|
|
16906
|
+
client,
|
|
16907
|
+
cacheContext,
|
|
16908
|
+
cteCache
|
|
16909
|
+
);
|
|
16437
16910
|
const hiddenQualifiedAliases = /* @__PURE__ */ new Set();
|
|
16438
16911
|
const withEffectiveAlias = (table) => {
|
|
16439
16912
|
if (table.alias !== null || table.cteName === null) return table;
|
|
@@ -16505,10 +16978,14 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16505
16978
|
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
16506
16979
|
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
16507
16980
|
validateKlikePushdownPlan(pushdownPlan);
|
|
16508
|
-
const
|
|
16981
|
+
const scalarCache = await resolveScalarColumns(
|
|
16982
|
+
stmt.columns,
|
|
16983
|
+
client,
|
|
16984
|
+
effectiveOptions,
|
|
16985
|
+
cacheContext,
|
|
16986
|
+
cteCache
|
|
16987
|
+
);
|
|
16509
16988
|
const orderByMetaPromise = Promise.resolve(orderMeta);
|
|
16510
|
-
scalarCachePromise.catch(() => {
|
|
16511
|
-
});
|
|
16512
16989
|
orderByMetaPromise.catch(() => {
|
|
16513
16990
|
});
|
|
16514
16991
|
const tables = /* @__PURE__ */ new Map();
|
|
@@ -16528,7 +17005,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16528
17005
|
effectiveOptions.onLimitReached ?? "error",
|
|
16529
17006
|
warnings,
|
|
16530
17007
|
pushdownPlan.mainCondition,
|
|
16531
|
-
whereCapability.capability === "EXACT_PUSHDOWN"
|
|
17008
|
+
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
17009
|
+
plainGroupByPlan
|
|
16532
17010
|
));
|
|
16533
17011
|
tables.set(stmt.from.alias, mainRecords);
|
|
16534
17012
|
}
|
|
@@ -16548,7 +17026,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16548
17026
|
parallel,
|
|
16549
17027
|
effectiveOptions.onLimitReached ?? "error",
|
|
16550
17028
|
warnings,
|
|
16551
|
-
pushDownCond
|
|
17029
|
+
pushDownCond,
|
|
17030
|
+
plainGroupByPlan
|
|
16552
17031
|
);
|
|
16553
17032
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
16554
17033
|
stmt,
|
|
@@ -16559,15 +17038,16 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16559
17038
|
false,
|
|
16560
17039
|
effectiveOptions.onLimitReached ?? "error",
|
|
16561
17040
|
warnings,
|
|
16562
|
-
pushDownCond
|
|
17041
|
+
pushDownCond,
|
|
17042
|
+
true,
|
|
17043
|
+
plainGroupByPlan
|
|
16563
17044
|
);
|
|
16564
17045
|
tables.set(join2.table.alias, joinRecords);
|
|
16565
17046
|
}
|
|
16566
17047
|
}));
|
|
16567
17048
|
await Promise.all(joinFetches);
|
|
16568
|
-
const scalarCache = await scalarCachePromise;
|
|
16569
17049
|
const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
|
|
16570
|
-
const
|
|
17050
|
+
const sourceColumns2 = stmt.joins.length === 0 && stmt.from.cteName != null ? requireMaterializedTable(stmt.from.cteName).columns : void 0;
|
|
16571
17051
|
const { rows, columns } = runFullScan({
|
|
16572
17052
|
tables,
|
|
16573
17053
|
stmt,
|
|
@@ -16581,10 +17061,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
16581
17061
|
havingFieldSemanticsResolver,
|
|
16582
17062
|
aggregateSortKindResolver,
|
|
16583
17063
|
appliedKlikes: pushdownPlan.appliedKlikes,
|
|
16584
|
-
sourceColumns,
|
|
17064
|
+
sourceColumns: sourceColumns2,
|
|
16585
17065
|
tableColumns,
|
|
16586
17066
|
hiddenQualifiedAliases,
|
|
16587
|
-
resolvedGroupingSpec
|
|
17067
|
+
resolvedGroupingSpec,
|
|
17068
|
+
plainGroupByPlan
|
|
16588
17069
|
});
|
|
16589
17070
|
return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
|
|
16590
17071
|
}
|
|
@@ -16593,8 +17074,8 @@ function processRowToKintoneRecord(row) {
|
|
|
16593
17074
|
Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
|
|
16594
17075
|
);
|
|
16595
17076
|
}
|
|
16596
|
-
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true) {
|
|
16597
|
-
const fields = selectToFetchAllFields(stmt, table);
|
|
17077
|
+
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true, plainGroupByPlan) {
|
|
17078
|
+
const fields = selectToFetchAllFields(stmt, table, plainGroupByPlan);
|
|
16598
17079
|
const onTruncate = (max) => {
|
|
16599
17080
|
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
17081
|
};
|
|
@@ -16708,7 +17189,7 @@ function splitChunks(items, size) {
|
|
|
16708
17189
|
var JOIN_IN_CHUNK_SIZE = 50;
|
|
16709
17190
|
var JOIN_IN_MAX_CHUNKS = 6;
|
|
16710
17191
|
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) {
|
|
17192
|
+
async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxRecords, parallel, onLimit, warnings, pushDownCond = null, plainGroupByPlan) {
|
|
16712
17193
|
if (join2.type !== "INNER") return null;
|
|
16713
17194
|
if (!join2.table.alias) return null;
|
|
16714
17195
|
if (join2.table.subtableCode) return null;
|
|
@@ -16745,7 +17226,7 @@ async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxR
|
|
|
16745
17226
|
);
|
|
16746
17227
|
return null;
|
|
16747
17228
|
}
|
|
16748
|
-
const fields = selectToFetchAllFields(stmt, join2.table);
|
|
17229
|
+
const fields = selectToFetchAllFields(stmt, join2.table, plainGroupByPlan);
|
|
16749
17230
|
const onTruncate = (max) => {
|
|
16750
17231
|
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
17232
|
};
|
|
@@ -16787,9 +17268,6 @@ function setScopedCacheValue(root, cacheContext, appId, value) {
|
|
|
16787
17268
|
}
|
|
16788
17269
|
scoped.set(appId, value);
|
|
16789
17270
|
}
|
|
16790
|
-
function isSystemLikeFieldCode(code) {
|
|
16791
|
-
return code.startsWith("_") || code.startsWith("$");
|
|
16792
|
-
}
|
|
16793
17271
|
async function getFieldsCached(appId, client, cacheContext) {
|
|
16794
17272
|
const cached = getScopedCacheValue(fieldInfoCache, cacheContext, appId);
|
|
16795
17273
|
if (cached) return cached;
|
|
@@ -19985,6 +20463,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
19985
20463
|
};
|
|
19986
20464
|
const capabilities = /* @__PURE__ */ new Map();
|
|
19987
20465
|
const orderPlans = /* @__PURE__ */ new Map();
|
|
20466
|
+
const plainGroupByPlans = /* @__PURE__ */ new Map();
|
|
19988
20467
|
const seen = /* @__PURE__ */ new Set();
|
|
19989
20468
|
const sharedRelativeDatePlan = relativeDatePlan ?? await resolveRelativeDateExecutionPlan(query, tracedClient, cacheContext);
|
|
19990
20469
|
const relativeNodeFor = (source) => sharedRelativeDatePlan.nodes.find(
|
|
@@ -20002,6 +20481,15 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
20002
20481
|
if (typed["type"] === "SELECT") {
|
|
20003
20482
|
const select = node;
|
|
20004
20483
|
await validateSelectGroupingPlanning(select, tracedClient, cacheContext);
|
|
20484
|
+
const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
|
|
20485
|
+
if (normalizeGroupingSpec(select).type === "PLAIN" && !hasUnmaterializedSource) {
|
|
20486
|
+
const plainPlan = await buildRuntimePlainGroupByPlan(
|
|
20487
|
+
select,
|
|
20488
|
+
tracedClient,
|
|
20489
|
+
cacheContext
|
|
20490
|
+
);
|
|
20491
|
+
if (plainPlan) plainGroupByPlans.set(select, plainPlan);
|
|
20492
|
+
}
|
|
20005
20493
|
const physicalApps = [select.from, ...select.joins.map((join2) => join2.table)].filter((table) => table.cteName === null).map((table) => table.appId);
|
|
20006
20494
|
const needsWhereSchema = whereNeedsFieldMetadata(select.where);
|
|
20007
20495
|
if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
@@ -20022,7 +20510,6 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
20022
20510
|
}
|
|
20023
20511
|
}
|
|
20024
20512
|
}
|
|
20025
|
-
const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
|
|
20026
20513
|
if (hasCanonicalOrder(select) && !hasUnmaterializedSource && relativeNode?.allowed !== false) {
|
|
20027
20514
|
const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
|
|
20028
20515
|
orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
@@ -20136,6 +20623,7 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
20136
20623
|
return {
|
|
20137
20624
|
capabilities,
|
|
20138
20625
|
orderPlans,
|
|
20626
|
+
plainGroupByPlans,
|
|
20139
20627
|
fieldApps,
|
|
20140
20628
|
processStatusApps,
|
|
20141
20629
|
numberPrecisionApps,
|
|
@@ -20258,6 +20746,32 @@ function relativeDateExplainLines(plan) {
|
|
|
20258
20746
|
}
|
|
20259
20747
|
const lines = [];
|
|
20260
20748
|
for (const node of plan.nodes) {
|
|
20749
|
+
const fullScanExactPlan = node.fullScanExactPlan;
|
|
20750
|
+
if (node.allowed && node.allowForm === "FULL_SCAN_EXACT" && fullScanExactPlan) {
|
|
20751
|
+
for (const leaf of fullScanExactPlan.prefilterPlan.exactRelativeLeaves) {
|
|
20752
|
+
const functionName = leaf.right.type === "KINTONE_FUNC" ? leaf.right.name : "(unknown)";
|
|
20753
|
+
const field = leaf.left.type === "FIELD" ? leaf.left.field : void 0;
|
|
20754
|
+
const operator = relativeReasonOperator(leaf.op);
|
|
20755
|
+
const detail = node.capability?.reasons.find(
|
|
20756
|
+
(reason) => reason.functionName === functionName && (field === void 0 || reason.field === field) && reason.operator === operator
|
|
20757
|
+
);
|
|
20758
|
+
lines.push(
|
|
20759
|
+
` relative date function: ${functionName}`,
|
|
20760
|
+
" relative date evaluation: kintone server whole-WHERE exact",
|
|
20761
|
+
` field: ${detail?.field ?? field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
20762
|
+
` operator: ${detail?.operator ?? operator}`
|
|
20763
|
+
);
|
|
20764
|
+
}
|
|
20765
|
+
const wholeWhereQuery = fullScanExactPlan.serializedWholeWhere;
|
|
20766
|
+
lines.push(
|
|
20767
|
+
" where capability: EXACT_PUSHDOWN",
|
|
20768
|
+
` server predicate: ${wholeWhereQuery}`,
|
|
20769
|
+
" client residual: (none)",
|
|
20770
|
+
" relative date client evaluations: 0",
|
|
20771
|
+
` kintone query: ${wholeWhereQuery}`
|
|
20772
|
+
);
|
|
20773
|
+
continue;
|
|
20774
|
+
}
|
|
20261
20775
|
const prefilterPlan = node.prefilterPlan;
|
|
20262
20776
|
if (node.allowed && prefilterPlan?.prefilterWhere && prefilterPlan.residualWhere) {
|
|
20263
20777
|
for (const leaf of prefilterPlan.exactRelativeLeaves) {
|
|
@@ -20459,7 +20973,8 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
|
|
|
20459
20973
|
analysis.orderPlans,
|
|
20460
20974
|
dmlMaxRows,
|
|
20461
20975
|
dmlMaxSubtableRows,
|
|
20462
|
-
maxRecords
|
|
20976
|
+
maxRecords,
|
|
20977
|
+
analysis.plainGroupByPlans
|
|
20463
20978
|
),
|
|
20464
20979
|
cursorMaxActive
|
|
20465
20980
|
)
|
|
@@ -20482,13 +20997,13 @@ function addCursorConcurrency(lines, cursorMaxActive) {
|
|
|
20482
20997
|
}
|
|
20483
20998
|
return result;
|
|
20484
20999
|
}
|
|
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);
|
|
21000
|
+
function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4, plainGroupByPlans) {
|
|
21001
|
+
if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans, plainGroupByPlans);
|
|
21002
|
+
if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans, plainGroupByPlans);
|
|
20488
21003
|
if (query.type === "INSERT") return buildInsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
20489
|
-
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
|
|
21004
|
+
if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
|
|
20490
21005
|
if (query.type === "UPSERT") return buildUpsertPlan(query, label, dmlMaxRows, dmlMaxSubtableRows);
|
|
20491
|
-
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans);
|
|
21006
|
+
if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
|
|
20492
21007
|
if (query.type === "UPDATE") return buildUpdatePlan(
|
|
20493
21008
|
query,
|
|
20494
21009
|
label,
|
|
@@ -20574,7 +21089,7 @@ function buildExplainPlan(query, label, capabilities, orderPlans, dmlMaxRows = 1
|
|
|
20574
21089
|
` duplicateKey: preflight before lookup/write (requires load)`
|
|
20575
21090
|
];
|
|
20576
21091
|
}
|
|
20577
|
-
return buildSelectPlan(query, label, capabilities, orderPlans);
|
|
21092
|
+
return buildSelectPlan(query, label, capabilities, orderPlans, plainGroupByPlans);
|
|
20578
21093
|
}
|
|
20579
21094
|
function buildValidatePlan(stmt, label) {
|
|
20580
21095
|
const info = validateExplainInfo.get(stmt);
|
|
@@ -20601,9 +21116,10 @@ function buildValidatePlan(stmt, label) {
|
|
|
20601
21116
|
lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
|
|
20602
21117
|
return lines;
|
|
20603
21118
|
}
|
|
20604
|
-
function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
21119
|
+
function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans) {
|
|
20605
21120
|
const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
20606
21121
|
const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
21122
|
+
const plainGroupByPlan = plainGroupByPlans?.get(stmt) ?? (plainGroupByPlans ? [...plainGroupByPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
|
|
20607
21123
|
const mode = orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN" ? "FULL_SCAN" : resolveSelectMode(stmt);
|
|
20608
21124
|
const reasons = collectFullScanReasons(stmt);
|
|
20609
21125
|
if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
|
|
@@ -20628,6 +21144,29 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20628
21144
|
` grouping output rows: runtime checked (limit: ${groupingMetadata.outputRowLimit}, before HAVING/DISTINCT/LIMIT)`
|
|
20629
21145
|
);
|
|
20630
21146
|
}
|
|
21147
|
+
const normalizedGrouping = normalizeGroupingSpec(stmt);
|
|
21148
|
+
if (normalizedGrouping.type === "PLAIN") {
|
|
21149
|
+
const groupBy = normalizedGrouping.allItems;
|
|
21150
|
+
if (plainGroupByPlan) {
|
|
21151
|
+
plainGroupByPlan.items.forEach((item, index) => {
|
|
21152
|
+
const key = groupBy[index];
|
|
21153
|
+
if (key?.type !== "FIELD_NAME") return;
|
|
21154
|
+
if (item.kind === "PHYSICAL") {
|
|
21155
|
+
lines.push(
|
|
21156
|
+
` group key ${key.name}: PHYSICAL (source=${item.sourceIndex}, field=${item.fieldCode})`
|
|
21157
|
+
);
|
|
21158
|
+
}
|
|
21159
|
+
});
|
|
21160
|
+
} else if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) {
|
|
21161
|
+
for (const key of groupBy) {
|
|
21162
|
+
if (key.type === "FIELD_NAME") {
|
|
21163
|
+
lines.push(
|
|
21164
|
+
` group key ${key.name}: DEFERRED (materialized schema unavailable)`
|
|
21165
|
+
);
|
|
21166
|
+
}
|
|
21167
|
+
}
|
|
21168
|
+
}
|
|
21169
|
+
}
|
|
20631
21170
|
if (orderPlan) {
|
|
20632
21171
|
lines.push(` order plan: ${orderPlan.kind}`);
|
|
20633
21172
|
if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
|
|
@@ -20669,7 +21208,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20669
21208
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
20670
21209
|
} else {
|
|
20671
21210
|
const pushdownPlan = buildKlikePushdownPlan(stmt);
|
|
20672
|
-
const mainFields = selectToFetchAllFields(stmt, stmt.from);
|
|
21211
|
+
const mainFields = selectToFetchAllFields(stmt, stmt.from, plainGroupByPlan);
|
|
20673
21212
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
20674
21213
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
20675
21214
|
const mainCandidate = extractMainTypedPushdownCandidate(stmt);
|
|
@@ -20682,7 +21221,7 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20682
21221
|
}
|
|
20683
21222
|
lines.push(` fields: ${mainFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : mainFields.join(", ")}`);
|
|
20684
21223
|
for (const join2 of stmt.joins) {
|
|
20685
|
-
const joinFields = selectToFetchAllFields(stmt, join2.table);
|
|
21224
|
+
const joinFields = selectToFetchAllFields(stmt, join2.table, plainGroupByPlan);
|
|
20686
21225
|
const joinAliasStr = join2.table.alias ? ` AS ${join2.table.alias}` : "";
|
|
20687
21226
|
const joinType = join2.type === "INNER" ? "JOIN" : `${join2.type} JOIN`;
|
|
20688
21227
|
const joinPushDown = join2.table.alias ? pushdownPlan.joinConditions.get(join2.table.alias) ?? null : null;
|
|
@@ -20696,10 +21235,10 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20696
21235
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|
|
20697
21236
|
}
|
|
20698
21237
|
}
|
|
20699
|
-
lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
|
|
21238
|
+
lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans));
|
|
20700
21239
|
return lines;
|
|
20701
21240
|
}
|
|
20702
|
-
function buildUnionPlan(stmt, capabilities, orderPlans) {
|
|
21241
|
+
function buildUnionPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
|
|
20703
21242
|
const selects = [];
|
|
20704
21243
|
const collect = (u) => {
|
|
20705
21244
|
if (u.type === "SELECT") {
|
|
@@ -20713,25 +21252,34 @@ function buildUnionPlan(stmt, capabilities, orderPlans) {
|
|
|
20713
21252
|
const lines = [];
|
|
20714
21253
|
selects.forEach((sel, i) => {
|
|
20715
21254
|
if (i > 0) lines.push("");
|
|
20716
|
-
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
|
|
21255
|
+
lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20717
21256
|
});
|
|
20718
21257
|
return lines;
|
|
20719
21258
|
}
|
|
20720
|
-
function buildWithPlan(stmt, capabilities, orderPlans) {
|
|
21259
|
+
function buildWithPlan(stmt, capabilities, orderPlans, plainGroupByPlans) {
|
|
20721
21260
|
const lines = [];
|
|
20722
21261
|
for (const cte of stmt.ctes) {
|
|
20723
21262
|
if (cte.query.type === "SELECT") {
|
|
20724
|
-
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
|
|
21263
|
+
lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20725
21264
|
lines.push("");
|
|
20726
21265
|
}
|
|
20727
21266
|
}
|
|
20728
21267
|
if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
|
|
20729
|
-
lines.push(...buildExplainPlan(
|
|
21268
|
+
lines.push(...buildExplainPlan(
|
|
21269
|
+
stmt.query,
|
|
21270
|
+
"[main]",
|
|
21271
|
+
capabilities,
|
|
21272
|
+
orderPlans,
|
|
21273
|
+
100,
|
|
21274
|
+
DEFAULT_APPLY_MAX_SUBTABLE_ROWS,
|
|
21275
|
+
1e4,
|
|
21276
|
+
plainGroupByPlans
|
|
21277
|
+
));
|
|
20730
21278
|
}
|
|
20731
21279
|
if (canInlineSingleCte(stmt)) {
|
|
20732
21280
|
lines.push("");
|
|
20733
21281
|
const inlined = buildInlinedQuery(stmt);
|
|
20734
|
-
lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
|
|
21282
|
+
lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans, plainGroupByPlans));
|
|
20735
21283
|
}
|
|
20736
21284
|
return lines;
|
|
20737
21285
|
}
|
|
@@ -20764,7 +21312,7 @@ function collectFullScanReasons(stmt) {
|
|
|
20764
21312
|
r.push("ORDER BY \u306B\u5F0F");
|
|
20765
21313
|
return r;
|
|
20766
21314
|
}
|
|
20767
|
-
function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
21315
|
+
function collectSubqueryPlans(stmt, capabilities, orderPlans, plainGroupByPlans) {
|
|
20768
21316
|
const lines = [];
|
|
20769
21317
|
let idx = 1;
|
|
20770
21318
|
const visitWhere = (w) => {
|
|
@@ -20773,16 +21321,16 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
|
20773
21321
|
case "BINARY":
|
|
20774
21322
|
if (w.right.type === "SCALAR_SUBQUERY") {
|
|
20775
21323
|
lines.push("");
|
|
20776
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21324
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20777
21325
|
}
|
|
20778
21326
|
if (w.right.type === "SUBQUERY_IN_LIST") {
|
|
20779
21327
|
lines.push("");
|
|
20780
|
-
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21328
|
+
lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20781
21329
|
}
|
|
20782
21330
|
break;
|
|
20783
21331
|
case "EXISTS":
|
|
20784
21332
|
lines.push("");
|
|
20785
|
-
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21333
|
+
lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20786
21334
|
break;
|
|
20787
21335
|
case "LOGICAL":
|
|
20788
21336
|
visitWhere(w.left);
|
|
@@ -20802,7 +21350,7 @@ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
|
|
|
20802
21350
|
for (const col of stmt.columns) {
|
|
20803
21351
|
if (col.type === "SCALAR_SUBQUERY_COL") {
|
|
20804
21352
|
lines.push("");
|
|
20805
|
-
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
|
|
21353
|
+
lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans, plainGroupByPlans));
|
|
20806
21354
|
}
|
|
20807
21355
|
}
|
|
20808
21356
|
if (stmt.having) visitWhere(stmt.having);
|
|
@@ -20820,7 +21368,7 @@ function buildInsertPlan(stmt, label, dmlMaxRows = DEFAULT_APPLY_MAX_ROWS, dmlMa
|
|
|
20820
21368
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
20821
21369
|
return stmt.applyBlocks?.length ? [...lines, ...formatStaticApplyDiagnostic(buildStaticApplyDiagnostic(stmt, dmlMaxRows, dmlMaxSubtableRows))] : lines;
|
|
20822
21370
|
}
|
|
20823
|
-
function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
21371
|
+
function buildInsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans) {
|
|
20824
21372
|
const lines = [];
|
|
20825
21373
|
if (label) lines.push(label);
|
|
20826
21374
|
lines.push(` [INSERT SELECT]`);
|
|
@@ -20828,7 +21376,7 @@ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20828
21376
|
lines.push(` fields: ${stmt.fields.join(", ")}`);
|
|
20829
21377
|
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
21378
|
lines.push("");
|
|
20831
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
21379
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans));
|
|
20832
21380
|
return lines;
|
|
20833
21381
|
}
|
|
20834
21382
|
function buildUpdatePlan(stmt, label, capabilities, orderPlans, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, maxRecords = 1e4) {
|
|
@@ -20982,7 +21530,7 @@ function formatStaticApplyDiagnostic(diagnostic2) {
|
|
|
20982
21530
|
lines.push("records API: 0", "mutation API: 0");
|
|
20983
21531
|
return lines;
|
|
20984
21532
|
}
|
|
20985
|
-
function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
21533
|
+
function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans) {
|
|
20986
21534
|
const lines = [
|
|
20987
21535
|
...label ? [label] : [],
|
|
20988
21536
|
` [UPSERT SELECT]`,
|
|
@@ -20992,7 +21540,7 @@ function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
|
|
|
20992
21540
|
` 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
21541
|
``
|
|
20994
21542
|
];
|
|
20995
|
-
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
|
|
21543
|
+
lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans, plainGroupByPlans));
|
|
20996
21544
|
return lines;
|
|
20997
21545
|
}
|
|
20998
21546
|
function buildReorderPlan(stmt, label) {
|