@rex0220/kintone-sql-tools 3.56.2 → 3.57.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/README.md +2 -0
- package/dist-cli/ksql.js +655 -248
- package/dist-engine/index.cjs +14 -14
- package/dist-engine/index.mjs +14 -14
- package/dist-engine/ksql-engine.umd.js +14 -14
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +76 -47
- package/dist-engine/meta/esm.json +76 -47
- package/dist-engine/meta/umd.json +76 -47
- package/dist-mcp/ksql-mcp.js +671 -254
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -604,7 +604,7 @@ function expandCubeGroupingSets(items) {
|
|
|
604
604
|
if (expandedSetCount > Math.floor(B65_MAX_GROUPING_SETS / 2)) {
|
|
605
605
|
const rejectedSetCount = expandedSetCount * 2;
|
|
606
606
|
throw new Error(
|
|
607
|
-
`ArgumentError:
|
|
607
|
+
`ArgumentError: expanded grouping set count ${rejectedSetCount} exceeds limit ${B65_MAX_GROUPING_SETS} (reason=GROUPING_SET_LIMIT_EXCEEDED).`
|
|
608
608
|
);
|
|
609
609
|
}
|
|
610
610
|
expandedSetCount *= 2;
|
|
@@ -5622,26 +5622,488 @@ function evalGroupingRef(ref, row) {
|
|
|
5622
5622
|
return membership.has(canonicalId) ? "0" : "1";
|
|
5623
5623
|
}
|
|
5624
5624
|
|
|
5625
|
+
// src/core/systemFields.ts
|
|
5626
|
+
var APP_SYSTEM_FIELD_CODES = [
|
|
5627
|
+
"$id",
|
|
5628
|
+
"$revision",
|
|
5629
|
+
"\u30EC\u30B3\u30FC\u30C9\u756A\u53F7",
|
|
5630
|
+
"\u4F5C\u6210\u8005",
|
|
5631
|
+
"\u4F5C\u6210\u65E5\u6642",
|
|
5632
|
+
"\u66F4\u65B0\u8005",
|
|
5633
|
+
"\u66F4\u65B0\u65E5\u6642",
|
|
5634
|
+
"\u30B9\u30C6\u30FC\u30BF\u30B9",
|
|
5635
|
+
"\u4F5C\u696D\u8005"
|
|
5636
|
+
];
|
|
5637
|
+
function isSystemLikeFieldCode(code) {
|
|
5638
|
+
return code.startsWith("_") || code.startsWith("$");
|
|
5639
|
+
}
|
|
5640
|
+
|
|
5641
|
+
// src/core/optimization/plainGroupByPlan.ts
|
|
5642
|
+
var AGGREGATE_REFERENCE_PREFIX = /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/;
|
|
5643
|
+
function containsAggregateColumnNode(node) {
|
|
5644
|
+
if (node === null || typeof node !== "object") return false;
|
|
5645
|
+
if (Array.isArray(node)) return node.some(containsAggregateColumnNode);
|
|
5646
|
+
const value = node;
|
|
5647
|
+
if (value["type"] === "AGGREGATE" || value["type"] === "ARITH_AGG_COL") return true;
|
|
5648
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
|
|
5649
|
+
if (value["type"] === "FIELD" && typeof value["field"] === "string") {
|
|
5650
|
+
return AGGREGATE_REFERENCE_PREFIX.test(value["field"]);
|
|
5651
|
+
}
|
|
5652
|
+
return Object.values(value).some(containsAggregateColumnNode);
|
|
5653
|
+
}
|
|
5654
|
+
function classifyPreGroupAlias(column) {
|
|
5655
|
+
switch (column.type) {
|
|
5656
|
+
case "AGGREGATE":
|
|
5657
|
+
case "ARITH_AGG_COL":
|
|
5658
|
+
return "AGGREGATE_DEPENDENT";
|
|
5659
|
+
case "GROUPING_COL":
|
|
5660
|
+
case "WINDOW_COL":
|
|
5661
|
+
return "POST_GROUP_ONLY";
|
|
5662
|
+
case "VARIABLE_COL":
|
|
5663
|
+
throw new Error("InternalError: unresolved VARIABLE_COL reached GROUP BY alias planning.");
|
|
5664
|
+
case "WILDCARD":
|
|
5665
|
+
case "PARENT_WILDCARD":
|
|
5666
|
+
case "FIELD":
|
|
5667
|
+
case "LITERAL_COL":
|
|
5668
|
+
case "ARITH_COL":
|
|
5669
|
+
case "CASE_COL":
|
|
5670
|
+
case "STRFUNC_COL":
|
|
5671
|
+
case "SCALAR_VALUE_COL":
|
|
5672
|
+
case "SCALAR_SUBQUERY_COL":
|
|
5673
|
+
return containsAggregate2(column) || containsAggregateColumnNode(column) ? "AGGREGATE_DEPENDENT" : "SAFE";
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
var SUBTABLE_SYSTEM_COLUMNS = ["_pid", "_rid", "_idx"];
|
|
5677
|
+
function unique(values) {
|
|
5678
|
+
return [...new Set(values)];
|
|
5679
|
+
}
|
|
5680
|
+
function sourceColumns(input) {
|
|
5681
|
+
switch (input.kind) {
|
|
5682
|
+
case "APP":
|
|
5683
|
+
return unique([
|
|
5684
|
+
...input.fieldCodes,
|
|
5685
|
+
...APP_SYSTEM_FIELD_CODES
|
|
5686
|
+
]);
|
|
5687
|
+
case "SUBTABLE":
|
|
5688
|
+
return unique([
|
|
5689
|
+
...input.childFieldCodes,
|
|
5690
|
+
...SUBTABLE_SYSTEM_COLUMNS,
|
|
5691
|
+
...input.parentFieldCodes.map((field) => `_p.${field}`)
|
|
5692
|
+
]);
|
|
5693
|
+
case "MATERIALIZED":
|
|
5694
|
+
return unique(input.columns);
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
function resolvePlainGroupBySourceSchemas(stmt, lookup) {
|
|
5698
|
+
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
5699
|
+
return sources.map((source, sourceIndex) => ({
|
|
5700
|
+
sourceIndex,
|
|
5701
|
+
qualifier: source.alias ?? source.cteName,
|
|
5702
|
+
columns: sourceColumns(lookup(source, sourceIndex))
|
|
5703
|
+
}));
|
|
5704
|
+
}
|
|
5705
|
+
function parseGroupName(name) {
|
|
5706
|
+
if (name.startsWith("_p.")) return { qualifier: null, fieldCode: name };
|
|
5707
|
+
const dot = name.indexOf(".");
|
|
5708
|
+
if (dot <= 0 || dot === name.length - 1) {
|
|
5709
|
+
return { qualifier: null, fieldCode: name };
|
|
5710
|
+
}
|
|
5711
|
+
return { qualifier: name.slice(0, dot), fieldCode: name.slice(dot + 1) };
|
|
5712
|
+
}
|
|
5713
|
+
function runtimeKey(source, fieldCode) {
|
|
5714
|
+
return source.qualifier === null ? fieldCode : `${source.qualifier}.${fieldCode}`;
|
|
5715
|
+
}
|
|
5716
|
+
function resolvePlainFieldReference(ref, schemas) {
|
|
5717
|
+
const name = ref.tableAlias === null ? ref.field : `${ref.tableAlias}.${ref.field}`;
|
|
5718
|
+
const parsed = ref.tableAlias === null ? parseGroupName(ref.field) : { qualifier: ref.tableAlias, fieldCode: ref.field };
|
|
5719
|
+
const candidateSources = parsed.qualifier === null ? schemas : schemas.filter((source2) => source2.qualifier === parsed.qualifier);
|
|
5720
|
+
const physical = candidateSources.filter((source2) => source2.columns.includes(parsed.fieldCode));
|
|
5721
|
+
if (physical.length > 1) return { kind: "AMBIGUOUS", name };
|
|
5722
|
+
if (physical.length === 0) return { kind: "UNKNOWN", name };
|
|
5723
|
+
const source = physical[0];
|
|
5724
|
+
return {
|
|
5725
|
+
kind: "PHYSICAL",
|
|
5726
|
+
sourceIndex: source.sourceIndex,
|
|
5727
|
+
fieldCode: parsed.fieldCode,
|
|
5728
|
+
runtimeKey: runtimeKey(source, parsed.fieldCode)
|
|
5729
|
+
};
|
|
5730
|
+
}
|
|
5731
|
+
function explicitAlias(column) {
|
|
5732
|
+
return "alias" in column && typeof column.alias === "string" ? column.alias : null;
|
|
5733
|
+
}
|
|
5734
|
+
function resolveFieldName(name, columns, schemas) {
|
|
5735
|
+
const parsed = parseGroupName(name);
|
|
5736
|
+
const candidateSources = parsed.qualifier === null ? schemas : schemas.filter((source) => source.qualifier === parsed.qualifier);
|
|
5737
|
+
const physical = candidateSources.filter((source) => source.columns.includes(parsed.fieldCode));
|
|
5738
|
+
if (physical.length > 1) {
|
|
5739
|
+
throw new Error(
|
|
5740
|
+
`ArgumentError: GROUP BY field ${name} is ambiguous across multiple sources (reason=GROUP_BY_FIELD_AMBIGUOUS).`
|
|
5741
|
+
);
|
|
5742
|
+
}
|
|
5743
|
+
if (physical.length === 1) {
|
|
5744
|
+
const source = physical[0];
|
|
5745
|
+
return {
|
|
5746
|
+
kind: "PHYSICAL",
|
|
5747
|
+
sourceIndex: source.sourceIndex,
|
|
5748
|
+
fieldCode: parsed.fieldCode,
|
|
5749
|
+
runtimeKey: runtimeKey(source, parsed.fieldCode)
|
|
5750
|
+
};
|
|
5751
|
+
}
|
|
5752
|
+
if (parsed.qualifier !== null) return { kind: "UNKNOWN", name };
|
|
5753
|
+
const aliases = columns.flatMap(
|
|
5754
|
+
(column, columnIndex) => explicitAlias(column) === name ? [{ column, columnIndex }] : []
|
|
5755
|
+
);
|
|
5756
|
+
if (aliases.length > 1) return { kind: "ALIAS_REJECT", reason: "DUPLICATE" };
|
|
5757
|
+
if (aliases.length === 1) {
|
|
5758
|
+
const candidate = aliases[0];
|
|
5759
|
+
const classification = classifyPreGroupAlias(candidate.column);
|
|
5760
|
+
if (classification === "SAFE") {
|
|
5761
|
+
return { kind: "ALIAS_SAFE", columnIndex: candidate.columnIndex };
|
|
5762
|
+
}
|
|
5763
|
+
return {
|
|
5764
|
+
kind: "ALIAS_REJECT",
|
|
5765
|
+
reason: classification === "AGGREGATE_DEPENDENT" ? "AGGREGATE" : "POST_GROUP_ONLY"
|
|
5766
|
+
};
|
|
5767
|
+
}
|
|
5768
|
+
const aggregateSyntheticMatch = columns.some(
|
|
5769
|
+
(column) => column.type === "AGGREGATE" && column.alias === null && aggregateSyntheticName(column.func, column.distinct, column.arg) === name
|
|
5770
|
+
);
|
|
5771
|
+
if (aggregateSyntheticMatch) return { kind: "ALIAS_REJECT", reason: "AGGREGATE" };
|
|
5772
|
+
return { kind: "UNKNOWN", name };
|
|
5773
|
+
}
|
|
5774
|
+
function planPlainGroupByResolution(groupBy, columns, schemas) {
|
|
5775
|
+
return {
|
|
5776
|
+
items: groupBy.map(
|
|
5777
|
+
(key) => key.type === "FIELD_NAME" ? resolveFieldName(key.name, columns, schemas) : { kind: "EXPRESSION" }
|
|
5778
|
+
)
|
|
5779
|
+
};
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
// src/core/aggregateDependencyValidation.ts
|
|
5783
|
+
var NON_GROUPED_DEPENDENCY_REASON = "B65_NON_GROUPED_DEPENDENCY";
|
|
5784
|
+
var WRAPPER_EXPR = /* @__PURE__ */ new Map([
|
|
5785
|
+
["ARITH_COL", "expr"],
|
|
5786
|
+
["ARITH_AGG_COL", "expr"],
|
|
5787
|
+
["STRFUNC_COL", "expr"],
|
|
5788
|
+
["SCALAR_VALUE_COL", "expr"],
|
|
5789
|
+
["CASE_COL", "expr"],
|
|
5790
|
+
["ARITH_KEY", "expr"],
|
|
5791
|
+
["FUNC_KEY", "expr"],
|
|
5792
|
+
["FUNC_FIELD", "expr"],
|
|
5793
|
+
["ARITH_FIELD", "expr"],
|
|
5794
|
+
["CASE_FIELD", "expr"],
|
|
5795
|
+
["ARITH_VALUE", "expr"],
|
|
5796
|
+
["CASE_VALUE", "expr"],
|
|
5797
|
+
["AGG_FIELD", "expr"]
|
|
5798
|
+
]);
|
|
5799
|
+
function refFromName(name) {
|
|
5800
|
+
if (name.startsWith("_p.")) return { type: "FIELD", tableAlias: null, field: name };
|
|
5801
|
+
const dot = name.indexOf(".");
|
|
5802
|
+
return dot > 0 ? { type: "FIELD", tableAlias: name.slice(0, dot), field: name.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: name };
|
|
5803
|
+
}
|
|
5804
|
+
function fieldRefFromNode(value) {
|
|
5805
|
+
const type = value["type"];
|
|
5806
|
+
if (type === "FIELD_REF" && typeof value["field"] === "string") {
|
|
5807
|
+
return refFromName(value["field"]);
|
|
5808
|
+
}
|
|
5809
|
+
if (type === "AGG_GROUP_KEY" && typeof value["field"] === "string") {
|
|
5810
|
+
return {
|
|
5811
|
+
type: "FIELD",
|
|
5812
|
+
tableAlias: typeof value["tableAlias"] === "string" ? value["tableAlias"] : null,
|
|
5813
|
+
field: value["field"]
|
|
5814
|
+
};
|
|
5815
|
+
}
|
|
5816
|
+
if (type === "FIELD" && typeof value["field"] === "string") {
|
|
5817
|
+
if (value["aggregateRef"] !== void 0) return null;
|
|
5818
|
+
const tableAlias = typeof value["tableAlias"] === "string" ? value["tableAlias"] : null;
|
|
5819
|
+
return tableAlias === null ? refFromName(value["field"]) : {
|
|
5820
|
+
type: "FIELD",
|
|
5821
|
+
tableAlias,
|
|
5822
|
+
field: value["field"]
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5825
|
+
return null;
|
|
5826
|
+
}
|
|
5827
|
+
function isQueryBoundary(value) {
|
|
5828
|
+
return value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY" || value["type"] === "SCALAR_SUBQUERY_COL" || value["type"] === "SUBQUERY_IN_LIST" || value["type"] === "EXISTS";
|
|
5829
|
+
}
|
|
5830
|
+
function isAggregateBoundary(value) {
|
|
5831
|
+
return value["type"] === "AGGREGATE" || value["type"] === "AGG_REF";
|
|
5832
|
+
}
|
|
5833
|
+
function isWindowBoundary(value) {
|
|
5834
|
+
return value["type"] === "WINDOW_COL";
|
|
5835
|
+
}
|
|
5836
|
+
function numberMeaning(value) {
|
|
5837
|
+
const number = value["value"];
|
|
5838
|
+
if (typeof number !== "number") return "?";
|
|
5839
|
+
if (Object.is(number, -0)) return "0";
|
|
5840
|
+
return String(number);
|
|
5841
|
+
}
|
|
5842
|
+
function canonicalExpression(node, resolveField2) {
|
|
5843
|
+
if (node === null) return "null";
|
|
5844
|
+
if (typeof node === "string" || typeof node === "boolean") return JSON.stringify(node);
|
|
5845
|
+
if (typeof node === "number") return String(node);
|
|
5846
|
+
if (Array.isArray(node)) {
|
|
5847
|
+
const items = node.map((item) => canonicalExpression(item, resolveField2));
|
|
5848
|
+
return items.some((item) => item === null) ? null : `[${items.join(",")}]`;
|
|
5849
|
+
}
|
|
5850
|
+
if (typeof node !== "object") return null;
|
|
5851
|
+
const value = node;
|
|
5852
|
+
const type = value["type"];
|
|
5853
|
+
if (type === "VARIABLE" || type === "VARIABLE_COL" || isQueryBoundary(value)) return null;
|
|
5854
|
+
if (type === "GROUP" && value["expr"] !== void 0) {
|
|
5855
|
+
return canonicalExpression(value["expr"], resolveField2);
|
|
5856
|
+
}
|
|
5857
|
+
const wrapper = typeof type === "string" ? WRAPPER_EXPR.get(type) : void 0;
|
|
5858
|
+
if (wrapper !== void 0) return canonicalExpression(value[wrapper], resolveField2);
|
|
5859
|
+
if (type === "LITERAL_COL") return `STRING:${JSON.stringify(value["value"] ?? "")}`;
|
|
5860
|
+
if (type === "STRING") return `STRING:${JSON.stringify(value["value"] ?? "")}`;
|
|
5861
|
+
if (type === "NUMBER") return `NUMBER:${numberMeaning(value)}`;
|
|
5862
|
+
if (type === "BOOLEAN") return `BOOLEAN:${String(value["value"])}`;
|
|
5863
|
+
const ref = fieldRefFromNode(value);
|
|
5864
|
+
if (ref !== null) {
|
|
5865
|
+
try {
|
|
5866
|
+
return `FIELD:${resolveField2(ref)}`;
|
|
5867
|
+
} catch {
|
|
5868
|
+
return null;
|
|
5869
|
+
}
|
|
5870
|
+
}
|
|
5871
|
+
if (type === "FIELD" && value["aggregateRef"] !== void 0) return null;
|
|
5872
|
+
const ignored = /* @__PURE__ */ new Set([
|
|
5873
|
+
"alias",
|
|
5874
|
+
"aliasDisplay",
|
|
5875
|
+
"raw",
|
|
5876
|
+
"separator",
|
|
5877
|
+
"distinct",
|
|
5878
|
+
"source"
|
|
5879
|
+
]);
|
|
5880
|
+
const parts = [];
|
|
5881
|
+
for (const key of Object.keys(value)) {
|
|
5882
|
+
if (key === "type" || ignored.has(key)) continue;
|
|
5883
|
+
const child = canonicalExpression(value[key], resolveField2);
|
|
5884
|
+
if (child === null) return null;
|
|
5885
|
+
parts.push(`${key}=${child}`);
|
|
5886
|
+
}
|
|
5887
|
+
const semanticType = type === "ARITH" || type === "SCALAR_ARITH" ? "ARITH" : String(type ?? "OBJECT");
|
|
5888
|
+
return `${semanticType}(${parts.join(",")})`;
|
|
5889
|
+
}
|
|
5890
|
+
function explicitAlias2(column) {
|
|
5891
|
+
return "alias" in column && typeof column.alias === "string" ? column.alias : null;
|
|
5892
|
+
}
|
|
5893
|
+
function aliasesByName(columns) {
|
|
5894
|
+
const result = /* @__PURE__ */ new Map();
|
|
5895
|
+
for (const column of columns) {
|
|
5896
|
+
const alias = explicitAlias2(column);
|
|
5897
|
+
if (alias === null) continue;
|
|
5898
|
+
result.set(alias, [...result.get(alias) ?? [], column]);
|
|
5899
|
+
}
|
|
5900
|
+
return result;
|
|
5901
|
+
}
|
|
5902
|
+
function displayField(ref) {
|
|
5903
|
+
return ref.tableAlias === null ? ref.field : `${ref.tableAlias}.${ref.field}`;
|
|
5904
|
+
}
|
|
5905
|
+
var AGGREGATE_SYNTHETIC_REFERENCE = /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/;
|
|
5906
|
+
function displayExpression(node) {
|
|
5907
|
+
if (node && typeof node === "object") {
|
|
5908
|
+
const value = node;
|
|
5909
|
+
const ref = fieldRefFromNode(value);
|
|
5910
|
+
if (ref !== null) return displayField(ref);
|
|
5911
|
+
if (value["type"] === "FIELD_NAME" && typeof value["name"] === "string") return value["name"];
|
|
5912
|
+
if (value["type"] === "WILDCARD") return "*";
|
|
5913
|
+
if (value["type"] === "PARENT_WILDCARD") return "_p.*";
|
|
5914
|
+
const alias = value["alias"];
|
|
5915
|
+
if (typeof alias === "string" && alias.length > 0) return alias;
|
|
5916
|
+
const wrapper = typeof value["type"] === "string" ? WRAPPER_EXPR.get(value["type"]) : void 0;
|
|
5917
|
+
if (wrapper !== void 0) {
|
|
5918
|
+
const inner = displayExpression(value[wrapper]);
|
|
5919
|
+
if (inner !== "\u5F0F") return inner;
|
|
5920
|
+
}
|
|
5921
|
+
const func = value["func"];
|
|
5922
|
+
if (typeof func === "string" && func.length > 0) return `${func}(...)`;
|
|
5923
|
+
}
|
|
5924
|
+
return "\u5F0F";
|
|
5925
|
+
}
|
|
5926
|
+
function dependencyError(clause, expression, dependency, policy) {
|
|
5927
|
+
const dependencyLabel = dependency === "WILDCARD" ? "wildcard" : displayField(dependency);
|
|
5928
|
+
const expressionLabel = dependency === "WILDCARD" ? displayExpression(expression) : displayExpression(expression);
|
|
5929
|
+
const groupText = policy.groupingLabel === null ? "GROUP BY \u304C\u306A\u3044\u305F\u3081\u5165\u529B\u5168\u4F53\u304C1\u30B0\u30EB\u30FC\u30D7\u306B\u306A\u308A" : `${policy.groupingLabel} \u306E\u5404\u30B0\u30EB\u30FC\u30D7\u3067\u306F`;
|
|
5930
|
+
const migration = dependency === "WILDCARD" ? "\u5FC5\u8981\u306A grouping \u5217\u3092\u660E\u793A\u3057\u3066 SELECT \u3057\u3066\u304F\u3060\u3055\u3044\u3002" : `\u300C${dependencyLabel}\u300D\u3092 MIN() \u306A\u3069\u306E\u96C6\u8A08\u95A2\u6570\u3067\u5305\u3080\u304B\u3001GROUP BY \u3078\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002`;
|
|
5931
|
+
const concreteMigration = dependency !== "WILDCARD" && dependency.tableAlias === null && expressionLabel === dependencyLabel && policy.migrationSourceSql !== void 0 ? policy.migrationGroupingFields && policy.migrationGroupingFields.length > 0 ? ` \u5B9F\u884C\u53EF\u80FD\u306A\u66F8\u304D\u63DB\u3048\u4F8B: \u300CSELECT ${policy.migrationGroupingFields.join(", ")}, MIN(${dependencyLabel}) FROM ${policy.migrationSourceSql} GROUP BY ${policy.migrationGroupingFields.join(", ")}\u300D\u3002` : ` \u5B9F\u884C\u53EF\u80FD\u306A\u66F8\u304D\u63DB\u3048\u4F8B: \u300CSELECT MIN(${dependencyLabel}) FROM ${policy.migrationSourceSql}\u300D\u3002` : "";
|
|
5932
|
+
return new Error(
|
|
5933
|
+
`ArgumentError: ${clause} \u5F0F\u300C${expressionLabel}\u300D\u306F\u96C6\u8A08\u3082\u30B0\u30EB\u30FC\u30D7\u5316\u3082\u3055\u308C\u3066\u3044\u307E\u305B\u3093\uFF08${policy.sourceLabel}\u3001\u975E\u30B0\u30EB\u30FC\u30D7\u5316\u4F9D\u5B58: ${dependencyLabel}\uFF09\u3002${groupText}\u3001\u3069\u306E\u884C\u306E\u5024\u3092\u8FD4\u3059\u304B\u6C7A\u307E\u308A\u307E\u305B\u3093\u3002${migration}${concreteMigration} (reason=${NON_GROUPED_DEPENDENCY_REASON})`
|
|
5934
|
+
);
|
|
5935
|
+
}
|
|
5936
|
+
function walkDependency(node, context) {
|
|
5937
|
+
if (node === null || typeof node !== "object") return;
|
|
5938
|
+
if (Array.isArray(node)) {
|
|
5939
|
+
for (const child of node) walkDependency(child, context);
|
|
5940
|
+
return;
|
|
5941
|
+
}
|
|
5942
|
+
const value = node;
|
|
5943
|
+
if (isQueryBoundary(value) || isAggregateBoundary(value) || isWindowBoundary(value)) return;
|
|
5944
|
+
if (value["type"] === "GROUPING_REF" || value["type"] === "GROUPING_FIELD" || value["type"] === "GROUPING_COL" || value["type"] === "GROUPING_KEY") return;
|
|
5945
|
+
if (value["type"] === "WILDCARD" || value["type"] === "PARENT_WILDCARD") {
|
|
5946
|
+
throw dependencyError(context.clause, context.expression, "WILDCARD", context.policy);
|
|
5947
|
+
}
|
|
5948
|
+
const ref = fieldRefFromNode(value);
|
|
5949
|
+
if (ref !== null) {
|
|
5950
|
+
if (context.clause !== "SELECT" && ref.tableAlias === null && AGGREGATE_SYNTHETIC_REFERENCE.test(ref.field)) return;
|
|
5951
|
+
if (context.clause !== "SELECT" && ref.tableAlias === null) {
|
|
5952
|
+
const targets = context.aliases.get(ref.field) ?? [];
|
|
5953
|
+
if (targets.length === 1 && !context.resolvingAliases.has(ref.field)) {
|
|
5954
|
+
const resolvingAliases = new Set(context.resolvingAliases);
|
|
5955
|
+
resolvingAliases.add(ref.field);
|
|
5956
|
+
walkDependency(targets[0], { ...context, resolvingAliases });
|
|
5957
|
+
return;
|
|
5958
|
+
}
|
|
5959
|
+
}
|
|
5960
|
+
const canonical2 = canonicalExpression(value, context.policy.resolveField);
|
|
5961
|
+
if (canonical2 !== null && context.policy.identities.has(canonical2)) return;
|
|
5962
|
+
const identity = `FIELD:${context.policy.resolveField(ref)}`;
|
|
5963
|
+
if (!context.policy.identities.has(identity)) {
|
|
5964
|
+
throw dependencyError(context.clause, context.expression, ref, context.policy);
|
|
5965
|
+
}
|
|
5966
|
+
return;
|
|
5967
|
+
}
|
|
5968
|
+
const canonical = canonicalExpression(value, context.policy.resolveField);
|
|
5969
|
+
if (canonical !== null && context.policy.identities.has(canonical)) return;
|
|
5970
|
+
const wrapper = typeof value["type"] === "string" ? WRAPPER_EXPR.get(value["type"]) : void 0;
|
|
5971
|
+
if (wrapper !== void 0) {
|
|
5972
|
+
walkDependency(value[wrapper], context);
|
|
5973
|
+
return;
|
|
5974
|
+
}
|
|
5975
|
+
for (const key of Object.keys(value)) {
|
|
5976
|
+
if (key === "type" || key === "alias" || key === "aliasDisplay" || key === "raw") continue;
|
|
5977
|
+
walkDependency(value[key], context);
|
|
5978
|
+
}
|
|
5979
|
+
}
|
|
5980
|
+
function validateAggregateDependencies(stmt, policy) {
|
|
5981
|
+
const aliases = aliasesByName(stmt.columns);
|
|
5982
|
+
for (const column of stmt.columns) {
|
|
5983
|
+
if (column.type === "WINDOW_COL") continue;
|
|
5984
|
+
walkDependency(column, {
|
|
5985
|
+
clause: "SELECT",
|
|
5986
|
+
expression: column,
|
|
5987
|
+
policy,
|
|
5988
|
+
aliases,
|
|
5989
|
+
resolvingAliases: /* @__PURE__ */ new Set()
|
|
5990
|
+
});
|
|
5991
|
+
}
|
|
5992
|
+
if (stmt.having !== null) {
|
|
5993
|
+
walkDependency(stmt.having, {
|
|
5994
|
+
clause: "HAVING",
|
|
5995
|
+
expression: stmt.having,
|
|
5996
|
+
policy,
|
|
5997
|
+
aliases,
|
|
5998
|
+
resolvingAliases: /* @__PURE__ */ new Set()
|
|
5999
|
+
});
|
|
6000
|
+
}
|
|
6001
|
+
for (const order of stmt.orderBy) {
|
|
6002
|
+
const key = order.key.type === "FIELD_NAME" ? refFromName(order.key.name) : order.key;
|
|
6003
|
+
walkDependency(key, {
|
|
6004
|
+
clause: "ORDER BY",
|
|
6005
|
+
expression: key,
|
|
6006
|
+
policy,
|
|
6007
|
+
aliases,
|
|
6008
|
+
resolvingAliases: /* @__PURE__ */ new Set()
|
|
6009
|
+
});
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
function hasAggregateNode(node) {
|
|
6013
|
+
if (node === null || typeof node !== "object") return false;
|
|
6014
|
+
if (Array.isArray(node)) return node.some(hasAggregateNode);
|
|
6015
|
+
const value = node;
|
|
6016
|
+
if (isQueryBoundary(value) || isWindowBoundary(value)) return false;
|
|
6017
|
+
if (isAggregateBoundary(value) || value["type"] === "ARITH_AGG_COL") return true;
|
|
6018
|
+
return Object.values(value).some(hasAggregateNode);
|
|
6019
|
+
}
|
|
6020
|
+
function isAggregateQueryBlock(stmt) {
|
|
6021
|
+
return normalizeGroupingSpec(stmt).type !== "NONE" || stmt.columns.some((column) => column.type !== "WINDOW_COL" && hasAggregateNode(column));
|
|
6022
|
+
}
|
|
6023
|
+
function groupByKeyNode(key) {
|
|
6024
|
+
if (key.type === "FIELD_NAME") return refFromName(key.name);
|
|
6025
|
+
return key.expr;
|
|
6026
|
+
}
|
|
6027
|
+
function buildOrdinaryDependencyPolicy(stmt, plan, schemas) {
|
|
6028
|
+
const normalized = normalizeGroupingSpec(stmt);
|
|
6029
|
+
if (normalized.type !== "PLAIN") {
|
|
6030
|
+
throw new Error("InternalError: ordinary dependency policy requires plain GROUP BY.");
|
|
6031
|
+
}
|
|
6032
|
+
const groupBy = normalized.allItems;
|
|
6033
|
+
const resolveField2 = (ref) => {
|
|
6034
|
+
const resolution = resolvePlainFieldReference(ref, schemas);
|
|
6035
|
+
if (resolution.kind === "AMBIGUOUS") {
|
|
6036
|
+
throw new Error(`ArgumentError: field ${resolution.name} is ambiguous across multiple sources.`);
|
|
6037
|
+
}
|
|
6038
|
+
if (resolution.kind === "UNKNOWN") return `unknown:${resolution.name}`;
|
|
6039
|
+
return `source:${resolution.sourceIndex}:${resolution.fieldCode}`;
|
|
6040
|
+
};
|
|
6041
|
+
const identities = /* @__PURE__ */ new Set();
|
|
6042
|
+
plan.items.forEach((item, index) => {
|
|
6043
|
+
if (item.kind === "PHYSICAL") {
|
|
6044
|
+
identities.add(`FIELD:source:${item.sourceIndex}:${item.fieldCode}`);
|
|
6045
|
+
return;
|
|
6046
|
+
}
|
|
6047
|
+
if (item.kind === "ALIAS_SAFE") {
|
|
6048
|
+
const key = canonicalExpression(stmt.columns[item.columnIndex], resolveField2);
|
|
6049
|
+
if (key !== null) identities.add(key);
|
|
6050
|
+
return;
|
|
6051
|
+
}
|
|
6052
|
+
if (item.kind === "EXPRESSION") {
|
|
6053
|
+
const key = canonicalExpression(groupByKeyNode(groupBy[index]), resolveField2);
|
|
6054
|
+
if (key !== null) identities.add(key);
|
|
6055
|
+
}
|
|
6056
|
+
});
|
|
6057
|
+
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
6058
|
+
const sourceSql = (source) => `APP${source.appId}${source.subtableCode != null ? `$${source.subtableCode}` : ""}`;
|
|
6059
|
+
const sourceLabel = sources.map((source) => source.cteName ?? sourceSql(source)).join(" / ");
|
|
6060
|
+
const groupingLabel = `GROUP BY ${groupBy.map(
|
|
6061
|
+
(key) => key.type === "FIELD_NAME" ? key.name : displayExpression(key)
|
|
6062
|
+
).join(", ")}`;
|
|
6063
|
+
const directAppSource = sources.length === 1 && sources[0].cteName === null ? `${sourceSql(sources[0])}${sources[0].alias && sources[0].alias !== sourceSql(sources[0]) ? ` ${sources[0].alias}` : ""}` : void 0;
|
|
6064
|
+
const migrationGroupingFields = groupBy.every(
|
|
6065
|
+
(key, index) => key.type === "FIELD_NAME" && plan.items[index]?.kind === "PHYSICAL"
|
|
6066
|
+
) ? groupBy.map((key) => key.name) : void 0;
|
|
6067
|
+
return {
|
|
6068
|
+
identities,
|
|
6069
|
+
resolveField: resolveField2,
|
|
6070
|
+
sourceLabel,
|
|
6071
|
+
groupingLabel,
|
|
6072
|
+
migrationSourceSql: directAppSource,
|
|
6073
|
+
migrationGroupingFields
|
|
6074
|
+
};
|
|
6075
|
+
}
|
|
6076
|
+
function validateAggregateDependenciesStatic(stmt) {
|
|
6077
|
+
if (normalizeGroupingSpec(stmt).type !== "NONE" || !isAggregateQueryBlock(stmt)) return;
|
|
6078
|
+
const policy = {
|
|
6079
|
+
identities: /* @__PURE__ */ new Set(),
|
|
6080
|
+
resolveField: (ref) => `ast:${displayField(ref)}`,
|
|
6081
|
+
sourceLabel: stmt.from.cteName ?? `APP${stmt.from.appId}`,
|
|
6082
|
+
groupingLabel: null,
|
|
6083
|
+
migrationSourceSql: stmt.from.cteName === null && stmt.joins.length === 0 ? `APP${stmt.from.appId}${stmt.from.alias && stmt.from.alias !== `APP${stmt.from.appId}` ? ` ${stmt.from.alias}` : ""}` : void 0
|
|
6084
|
+
};
|
|
6085
|
+
validateAggregateDependencies(stmt, policy);
|
|
6086
|
+
}
|
|
6087
|
+
function canonicalGroupingFieldIdentity(canonicalId) {
|
|
6088
|
+
return `FIELD:${canonicalId}`;
|
|
6089
|
+
}
|
|
6090
|
+
|
|
5625
6091
|
// src/core/groupingValidation.ts
|
|
5626
6092
|
var enforceGroupingPlanningCandidateLimits = (facts) => {
|
|
5627
6093
|
if (facts.expandedSetCount > B65_MAX_GROUPING_SETS) {
|
|
5628
6094
|
throw new Error(
|
|
5629
|
-
`ArgumentError:
|
|
6095
|
+
`ArgumentError: expanded grouping set count ${facts.expandedSetCount} exceeds limit ${B65_MAX_GROUPING_SETS} (reason=GROUPING_SET_LIMIT_EXCEEDED).`
|
|
5630
6096
|
);
|
|
5631
6097
|
}
|
|
5632
6098
|
if (facts.canonicalItemCount > B65_MAX_GROUPING_ITEMS) {
|
|
5633
6099
|
throw new Error(
|
|
5634
|
-
`ArgumentError:
|
|
6100
|
+
`ArgumentError: canonical grouping item count ${facts.canonicalItemCount} exceeds limit ${B65_MAX_GROUPING_ITEMS} (reason=GROUPING_ITEM_LIMIT_EXCEEDED).`
|
|
5635
6101
|
);
|
|
5636
6102
|
}
|
|
5637
6103
|
};
|
|
5638
|
-
function
|
|
6104
|
+
function displayField2(field) {
|
|
5639
6105
|
return field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
|
|
5640
6106
|
}
|
|
5641
|
-
function refFromName(name) {
|
|
5642
|
-
const dot = name.indexOf(".");
|
|
5643
|
-
return dot > 0 ? { type: "FIELD", tableAlias: name.slice(0, dot), field: name.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: name };
|
|
5644
|
-
}
|
|
5645
6107
|
function collectGroupingRefs(node, out) {
|
|
5646
6108
|
if (node === null || typeof node !== "object") return;
|
|
5647
6109
|
if (Array.isArray(node)) {
|
|
@@ -5670,30 +6132,6 @@ function collectAggregateArgumentGroupingRefs(node, out) {
|
|
|
5670
6132
|
}
|
|
5671
6133
|
Object.values(value).forEach((item) => collectAggregateArgumentGroupingRefs(item, out));
|
|
5672
6134
|
}
|
|
5673
|
-
function collectNonAggregateFieldRefs(node, out) {
|
|
5674
|
-
if (node === null || typeof node !== "object") return;
|
|
5675
|
-
if (Array.isArray(node)) {
|
|
5676
|
-
node.forEach((item) => collectNonAggregateFieldRefs(item, out));
|
|
5677
|
-
return;
|
|
5678
|
-
}
|
|
5679
|
-
const value = node;
|
|
5680
|
-
const type = value["type"];
|
|
5681
|
-
if (type === "SELECT" || type === "SCALAR_SUBQUERY" || type === "GROUPING_REF" || type === "AGG_REF" || type === "AGG_ARITH") return;
|
|
5682
|
-
if (type === "FIELD" && value["aggregateRef"] !== void 0) return;
|
|
5683
|
-
if (type === "FIELD" && typeof value["field"] === "string") {
|
|
5684
|
-
out.push({
|
|
5685
|
-
type: "FIELD",
|
|
5686
|
-
tableAlias: typeof value["tableAlias"] === "string" ? value["tableAlias"] : null,
|
|
5687
|
-
field: value["field"]
|
|
5688
|
-
});
|
|
5689
|
-
return;
|
|
5690
|
-
}
|
|
5691
|
-
if (type === "FIELD_REF" && typeof value["field"] === "string") {
|
|
5692
|
-
out.push(refFromName(value["field"]));
|
|
5693
|
-
return;
|
|
5694
|
-
}
|
|
5695
|
-
Object.values(value).forEach((item) => collectNonAggregateFieldRefs(item, out));
|
|
5696
|
-
}
|
|
5697
6135
|
function containsAggregate2(node) {
|
|
5698
6136
|
if (node === null || typeof node !== "object") return false;
|
|
5699
6137
|
if (Array.isArray(node)) return node.some(containsAggregate2);
|
|
@@ -5710,41 +6148,18 @@ function isAggregateMaterializedAlias(column) {
|
|
|
5710
6148
|
}
|
|
5711
6149
|
return false;
|
|
5712
6150
|
}
|
|
5713
|
-
function outputAliases(columns) {
|
|
5714
|
-
return new Set(columns.flatMap(
|
|
5715
|
-
(column) => "alias" in column && typeof column.alias === "string" ? [column.alias] : []
|
|
5716
|
-
));
|
|
5717
|
-
}
|
|
5718
|
-
function isAggregateSyntheticReference(ref) {
|
|
5719
|
-
return ref.tableAlias === null && /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/.test(ref.field);
|
|
5720
|
-
}
|
|
5721
6151
|
function validateGroupingRefMembership(ref, resolve2, canonicalItems) {
|
|
5722
6152
|
const resolved = resolve2(ref.field);
|
|
5723
6153
|
if (!resolved.physical) {
|
|
5724
|
-
throw new Error(`ArgumentError:
|
|
6154
|
+
throw new Error(`ArgumentError: grouping reference ${displayField2(ref.field)} must resolve to a physical APP field.`);
|
|
5725
6155
|
}
|
|
5726
6156
|
if (!canonicalItems.has(resolved.canonicalId)) {
|
|
5727
6157
|
throw new Error(
|
|
5728
|
-
`ArgumentError:
|
|
6158
|
+
`ArgumentError: GROUPING argument ${displayField2(ref.field)} is not present in grouping allItems (reason=B65_GROUPING_ARG_NOT_ITEM).`
|
|
5729
6159
|
);
|
|
5730
6160
|
}
|
|
5731
6161
|
bindGroupingRefCanonicalId(ref, resolved.canonicalId);
|
|
5732
6162
|
}
|
|
5733
|
-
function validateDependency(ref, resolve2, canonicalItems, context) {
|
|
5734
|
-
const resolved = resolve2(ref);
|
|
5735
|
-
if (!resolved.physical || !canonicalItems.has(resolved.canonicalId)) {
|
|
5736
|
-
throw new Error(
|
|
5737
|
-
`ArgumentError: B65 non-aggregate field ${displayField(ref)} in ${context} is not a grouping item (reason=B65_NON_GROUPED_DEPENDENCY).`
|
|
5738
|
-
);
|
|
5739
|
-
}
|
|
5740
|
-
}
|
|
5741
|
-
function keyDependencies(key) {
|
|
5742
|
-
if (key.type === "GROUPING_KEY") return [];
|
|
5743
|
-
if (key.type === "FIELD_NAME") return [refFromName(key.name)];
|
|
5744
|
-
const refs = [];
|
|
5745
|
-
collectNonAggregateFieldRefs(key, refs);
|
|
5746
|
-
return refs;
|
|
5747
|
-
}
|
|
5748
6163
|
function validateGroupingStatic(stmt) {
|
|
5749
6164
|
const normalized = normalizeGroupingSpec(stmt);
|
|
5750
6165
|
const groupingRefs = [];
|
|
@@ -5763,25 +6178,28 @@ function validateGroupingStatic(stmt) {
|
|
|
5763
6178
|
}
|
|
5764
6179
|
if (forbiddenGroupingRefs.length > 0) {
|
|
5765
6180
|
throw new Error(
|
|
5766
|
-
"ArgumentError:
|
|
6181
|
+
"ArgumentError: GROUPING() is not allowed in WHERE, JOIN, window, aggregate arguments, or DML expressions."
|
|
5767
6182
|
);
|
|
5768
6183
|
}
|
|
5769
6184
|
if (normalized.type !== "GROUPING_SETS") {
|
|
5770
6185
|
if (groupingRefs.length > 0) {
|
|
5771
|
-
throw new Error("ArgumentError:
|
|
6186
|
+
throw new Error("ArgumentError: GROUPING() requires GROUP BY ROLLUP or GROUPING SETS.");
|
|
5772
6187
|
}
|
|
6188
|
+
validateAggregateDependenciesStatic(stmt);
|
|
5773
6189
|
return;
|
|
5774
6190
|
}
|
|
5775
6191
|
if (stmt.orderMode === "KINTONE_NATIVE") {
|
|
5776
|
-
throw new Error("ArgumentError:
|
|
6192
|
+
throw new Error("ArgumentError: KORDER BY is not supported with extended grouping.");
|
|
5777
6193
|
}
|
|
5778
6194
|
if (stmt.columns.some((column) => column.type === "WINDOW_COL")) {
|
|
5779
|
-
throw new Error("ArgumentError:
|
|
6195
|
+
throw new Error("ArgumentError: window functions are not supported with extended grouping.");
|
|
5780
6196
|
}
|
|
5781
6197
|
if (stmt.columns.some(
|
|
5782
6198
|
(column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD"
|
|
5783
6199
|
)) {
|
|
5784
|
-
throw new Error(
|
|
6200
|
+
throw new Error(
|
|
6201
|
+
"ArgumentError: SELECT wildcard \u306F\u96C6\u8A08 query \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u5FC5\u8981\u306A grouping \u5217\u3092\u660E\u793A\u3057\u3066\u304F\u3060\u3055\u3044 (reason=B65_NON_GROUPED_DEPENDENCY)."
|
|
6202
|
+
);
|
|
5785
6203
|
}
|
|
5786
6204
|
}
|
|
5787
6205
|
function validateGroupingPlanning(stmt, resolve2, planningGuardHook = () => void 0) {
|
|
@@ -5802,7 +6220,7 @@ function validateGroupingPlanning(stmt, resolve2, planningGuardHook = () => void
|
|
|
5802
6220
|
for (const item of resolvedSpec.allItems) {
|
|
5803
6221
|
const resolved = item;
|
|
5804
6222
|
if (!resolved.physical) {
|
|
5805
|
-
throw new Error(`ArgumentError:
|
|
6223
|
+
throw new Error(`ArgumentError: grouping item ${displayField2(item.field)} must resolve to a physical APP field.`);
|
|
5806
6224
|
}
|
|
5807
6225
|
if (!canonicalItems.has(resolved.canonicalId)) {
|
|
5808
6226
|
canonicalItems.add(resolved.canonicalId);
|
|
@@ -5816,28 +6234,12 @@ function validateGroupingPlanning(stmt, resolve2, planningGuardHook = () => void
|
|
|
5816
6234
|
for (const ref of groupingRefs) {
|
|
5817
6235
|
validateGroupingRefMembership(ref, resolve2, canonicalItems);
|
|
5818
6236
|
}
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
for (const ref of refs) validateDependency(ref, resolve2, canonicalItems, "SELECT");
|
|
5826
|
-
}
|
|
5827
|
-
if (stmt.having) {
|
|
5828
|
-
const refs = [];
|
|
5829
|
-
collectNonAggregateFieldRefs(stmt.having, refs);
|
|
5830
|
-
for (const ref of refs) {
|
|
5831
|
-
if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
|
|
5832
|
-
validateDependency(ref, resolve2, canonicalItems, "HAVING");
|
|
5833
|
-
}
|
|
5834
|
-
}
|
|
5835
|
-
for (const order of stmt.orderBy) {
|
|
5836
|
-
for (const ref of keyDependencies(order.key)) {
|
|
5837
|
-
if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
|
|
5838
|
-
validateDependency(ref, resolve2, canonicalItems, "ORDER BY");
|
|
5839
|
-
}
|
|
5840
|
-
}
|
|
6237
|
+
validateAggregateDependencies(stmt, {
|
|
6238
|
+
identities: new Set([...canonicalItems].map(canonicalGroupingFieldIdentity)),
|
|
6239
|
+
resolveField: (ref) => resolve2(ref).canonicalId,
|
|
6240
|
+
sourceLabel: stmt.from.cteName ?? `APP${stmt.from.appId}`,
|
|
6241
|
+
groupingLabel: `GROUP BY ${normalized.source}`
|
|
6242
|
+
});
|
|
5841
6243
|
const collisionKeys = /* @__PURE__ */ new Set();
|
|
5842
6244
|
for (const item of resolvedItems) {
|
|
5843
6245
|
collisionKeys.add(item.directKey);
|
|
@@ -5849,7 +6251,7 @@ function validateGroupingPlanning(stmt, resolve2, planningGuardHook = () => void
|
|
|
5849
6251
|
if (alias === null) continue;
|
|
5850
6252
|
if (collisionKeys.has(alias)) {
|
|
5851
6253
|
throw new Error(
|
|
5852
|
-
`ArgumentError:
|
|
6254
|
+
`ArgumentError: aggregate alias ${alias} collides with a grouping runtime key (reason=B65_AGGREGATE_ALIAS_COLLISION).`
|
|
5853
6255
|
);
|
|
5854
6256
|
}
|
|
5855
6257
|
}
|
|
@@ -13077,148 +13479,6 @@ function buildKorderCursorQuery(stmt) {
|
|
|
13077
13479
|
return parts.join(" ");
|
|
13078
13480
|
}
|
|
13079
13481
|
|
|
13080
|
-
// src/core/systemFields.ts
|
|
13081
|
-
var APP_SYSTEM_FIELD_CODES = [
|
|
13082
|
-
"$id",
|
|
13083
|
-
"$revision",
|
|
13084
|
-
"\u30EC\u30B3\u30FC\u30C9\u756A\u53F7",
|
|
13085
|
-
"\u4F5C\u6210\u8005",
|
|
13086
|
-
"\u4F5C\u6210\u65E5\u6642",
|
|
13087
|
-
"\u66F4\u65B0\u8005",
|
|
13088
|
-
"\u66F4\u65B0\u65E5\u6642",
|
|
13089
|
-
"\u30B9\u30C6\u30FC\u30BF\u30B9",
|
|
13090
|
-
"\u4F5C\u696D\u8005"
|
|
13091
|
-
];
|
|
13092
|
-
function isSystemLikeFieldCode(code) {
|
|
13093
|
-
return code.startsWith("_") || code.startsWith("$");
|
|
13094
|
-
}
|
|
13095
|
-
|
|
13096
|
-
// src/core/optimization/plainGroupByPlan.ts
|
|
13097
|
-
var AGGREGATE_REFERENCE_PREFIX = /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/;
|
|
13098
|
-
function containsAggregateColumnNode(node) {
|
|
13099
|
-
if (node === null || typeof node !== "object") return false;
|
|
13100
|
-
if (Array.isArray(node)) return node.some(containsAggregateColumnNode);
|
|
13101
|
-
const value = node;
|
|
13102
|
-
if (value["type"] === "AGGREGATE" || value["type"] === "ARITH_AGG_COL") return true;
|
|
13103
|
-
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
|
|
13104
|
-
if (value["type"] === "FIELD" && typeof value["field"] === "string") {
|
|
13105
|
-
return AGGREGATE_REFERENCE_PREFIX.test(value["field"]);
|
|
13106
|
-
}
|
|
13107
|
-
return Object.values(value).some(containsAggregateColumnNode);
|
|
13108
|
-
}
|
|
13109
|
-
function classifyPreGroupAlias(column) {
|
|
13110
|
-
switch (column.type) {
|
|
13111
|
-
case "AGGREGATE":
|
|
13112
|
-
case "ARITH_AGG_COL":
|
|
13113
|
-
return "AGGREGATE_DEPENDENT";
|
|
13114
|
-
case "GROUPING_COL":
|
|
13115
|
-
case "WINDOW_COL":
|
|
13116
|
-
return "POST_GROUP_ONLY";
|
|
13117
|
-
case "VARIABLE_COL":
|
|
13118
|
-
throw new Error("InternalError: unresolved VARIABLE_COL reached GROUP BY alias planning.");
|
|
13119
|
-
case "WILDCARD":
|
|
13120
|
-
case "PARENT_WILDCARD":
|
|
13121
|
-
case "FIELD":
|
|
13122
|
-
case "LITERAL_COL":
|
|
13123
|
-
case "ARITH_COL":
|
|
13124
|
-
case "CASE_COL":
|
|
13125
|
-
case "STRFUNC_COL":
|
|
13126
|
-
case "SCALAR_VALUE_COL":
|
|
13127
|
-
case "SCALAR_SUBQUERY_COL":
|
|
13128
|
-
return containsAggregate2(column) || containsAggregateColumnNode(column) ? "AGGREGATE_DEPENDENT" : "SAFE";
|
|
13129
|
-
}
|
|
13130
|
-
}
|
|
13131
|
-
var SUBTABLE_SYSTEM_COLUMNS = ["_pid", "_rid", "_idx"];
|
|
13132
|
-
function unique(values) {
|
|
13133
|
-
return [...new Set(values)];
|
|
13134
|
-
}
|
|
13135
|
-
function sourceColumns(input) {
|
|
13136
|
-
switch (input.kind) {
|
|
13137
|
-
case "APP":
|
|
13138
|
-
return unique([
|
|
13139
|
-
...input.fieldCodes,
|
|
13140
|
-
...APP_SYSTEM_FIELD_CODES
|
|
13141
|
-
]);
|
|
13142
|
-
case "SUBTABLE":
|
|
13143
|
-
return unique([
|
|
13144
|
-
...input.childFieldCodes,
|
|
13145
|
-
...SUBTABLE_SYSTEM_COLUMNS,
|
|
13146
|
-
...input.parentFieldCodes.map((field) => `_p.${field}`)
|
|
13147
|
-
]);
|
|
13148
|
-
case "MATERIALIZED":
|
|
13149
|
-
return unique(input.columns);
|
|
13150
|
-
}
|
|
13151
|
-
}
|
|
13152
|
-
function resolvePlainGroupBySourceSchemas(stmt, lookup) {
|
|
13153
|
-
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
13154
|
-
return sources.map((source, sourceIndex) => ({
|
|
13155
|
-
sourceIndex,
|
|
13156
|
-
qualifier: source.alias ?? source.cteName,
|
|
13157
|
-
columns: sourceColumns(lookup(source, sourceIndex))
|
|
13158
|
-
}));
|
|
13159
|
-
}
|
|
13160
|
-
function parseGroupName(name) {
|
|
13161
|
-
if (name.startsWith("_p.")) return { qualifier: null, fieldCode: name };
|
|
13162
|
-
const dot = name.indexOf(".");
|
|
13163
|
-
if (dot <= 0 || dot === name.length - 1) {
|
|
13164
|
-
return { qualifier: null, fieldCode: name };
|
|
13165
|
-
}
|
|
13166
|
-
return { qualifier: name.slice(0, dot), fieldCode: name.slice(dot + 1) };
|
|
13167
|
-
}
|
|
13168
|
-
function runtimeKey(source, fieldCode) {
|
|
13169
|
-
return source.qualifier === null ? fieldCode : `${source.qualifier}.${fieldCode}`;
|
|
13170
|
-
}
|
|
13171
|
-
function explicitAlias(column) {
|
|
13172
|
-
return "alias" in column && typeof column.alias === "string" ? column.alias : null;
|
|
13173
|
-
}
|
|
13174
|
-
function resolveFieldName(name, columns, schemas) {
|
|
13175
|
-
const parsed = parseGroupName(name);
|
|
13176
|
-
const candidateSources = parsed.qualifier === null ? schemas : schemas.filter((source) => source.qualifier === parsed.qualifier);
|
|
13177
|
-
const physical = candidateSources.filter((source) => source.columns.includes(parsed.fieldCode));
|
|
13178
|
-
if (physical.length > 1) {
|
|
13179
|
-
throw new Error(
|
|
13180
|
-
`ArgumentError: GROUP BY field ${name} is ambiguous across multiple sources (reason=GROUP_BY_FIELD_AMBIGUOUS).`
|
|
13181
|
-
);
|
|
13182
|
-
}
|
|
13183
|
-
if (physical.length === 1) {
|
|
13184
|
-
const source = physical[0];
|
|
13185
|
-
return {
|
|
13186
|
-
kind: "PHYSICAL",
|
|
13187
|
-
sourceIndex: source.sourceIndex,
|
|
13188
|
-
fieldCode: parsed.fieldCode,
|
|
13189
|
-
runtimeKey: runtimeKey(source, parsed.fieldCode)
|
|
13190
|
-
};
|
|
13191
|
-
}
|
|
13192
|
-
if (parsed.qualifier !== null) return { kind: "UNKNOWN", name };
|
|
13193
|
-
const aliases = columns.flatMap(
|
|
13194
|
-
(column, columnIndex) => explicitAlias(column) === name ? [{ column, columnIndex }] : []
|
|
13195
|
-
);
|
|
13196
|
-
if (aliases.length > 1) return { kind: "ALIAS_REJECT", reason: "DUPLICATE" };
|
|
13197
|
-
if (aliases.length === 1) {
|
|
13198
|
-
const candidate = aliases[0];
|
|
13199
|
-
const classification = classifyPreGroupAlias(candidate.column);
|
|
13200
|
-
if (classification === "SAFE") {
|
|
13201
|
-
return { kind: "ALIAS_SAFE", columnIndex: candidate.columnIndex };
|
|
13202
|
-
}
|
|
13203
|
-
return {
|
|
13204
|
-
kind: "ALIAS_REJECT",
|
|
13205
|
-
reason: classification === "AGGREGATE_DEPENDENT" ? "AGGREGATE" : "POST_GROUP_ONLY"
|
|
13206
|
-
};
|
|
13207
|
-
}
|
|
13208
|
-
const aggregateSyntheticMatch = columns.some(
|
|
13209
|
-
(column) => column.type === "AGGREGATE" && column.alias === null && aggregateSyntheticName(column.func, column.distinct, column.arg) === name
|
|
13210
|
-
);
|
|
13211
|
-
if (aggregateSyntheticMatch) return { kind: "ALIAS_REJECT", reason: "AGGREGATE" };
|
|
13212
|
-
return { kind: "UNKNOWN", name };
|
|
13213
|
-
}
|
|
13214
|
-
function planPlainGroupByResolution(groupBy, columns, schemas) {
|
|
13215
|
-
return {
|
|
13216
|
-
items: groupBy.map(
|
|
13217
|
-
(key) => key.type === "FIELD_NAME" ? resolveFieldName(key.name, columns, schemas) : { kind: "EXPRESSION" }
|
|
13218
|
-
)
|
|
13219
|
-
};
|
|
13220
|
-
}
|
|
13221
|
-
|
|
13222
13482
|
// src/core/emptyWildcardSchema.ts
|
|
13223
13483
|
var EMPTY_WILDCARD_FIELD_TYPE_POLICY = {
|
|
13224
13484
|
CALC: "RECORD",
|
|
@@ -17189,7 +17449,13 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
|
|
|
17189
17449
|
return { tempTable: stmt.name };
|
|
17190
17450
|
}
|
|
17191
17451
|
if (stmt.type === "EXPLAIN") {
|
|
17192
|
-
|
|
17452
|
+
const explainStmt = resolvedStmt;
|
|
17453
|
+
explainMaterializedTables.set(explainStmt, tempTables);
|
|
17454
|
+
try {
|
|
17455
|
+
return { result: await executeParsedStatement(explainStmt, client, options, cacheContext) };
|
|
17456
|
+
} finally {
|
|
17457
|
+
explainMaterializedTables.delete(explainStmt);
|
|
17458
|
+
}
|
|
17193
17459
|
}
|
|
17194
17460
|
if (resolvedStmt.type === "ASSERT") {
|
|
17195
17461
|
await executeAssert(resolvedStmt, client, options, cacheContext, tempTables);
|
|
@@ -17998,7 +18264,12 @@ function dedupeSubtableOwners(owners) {
|
|
|
17998
18264
|
return result;
|
|
17999
18265
|
}
|
|
18000
18266
|
async function buildRuntimePlainGroupByPlan(stmt, client, cacheContext, materializedTables) {
|
|
18001
|
-
|
|
18267
|
+
const normalized = normalizeGroupingSpec(stmt);
|
|
18268
|
+
if (!isAggregateQueryBlock(stmt) || normalized.type === "GROUPING_SETS") {
|
|
18269
|
+
return void 0;
|
|
18270
|
+
}
|
|
18271
|
+
if (normalized.type === "NONE") return void 0;
|
|
18272
|
+
const groupBy = normalized.allItems;
|
|
18002
18273
|
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
18003
18274
|
const subtableOwners = /* @__PURE__ */ new Map();
|
|
18004
18275
|
const inputs = await Promise.all(sources.map(async (source) => {
|
|
@@ -18036,9 +18307,9 @@ async function buildRuntimePlainGroupByPlan(stmt, client, cacheContext, material
|
|
|
18036
18307
|
stmt,
|
|
18037
18308
|
(_source, sourceIndex) => inputs[sourceIndex]
|
|
18038
18309
|
);
|
|
18039
|
-
const groupBy = stmt.groupBy;
|
|
18040
18310
|
const plan = planPlainGroupByResolution(groupBy, stmt.columns, schemas);
|
|
18041
18311
|
assertRuntimePlainGroupByPlan(stmt, groupBy, plan, subtableOwners);
|
|
18312
|
+
validateAggregateDependencies(stmt, buildOrdinaryDependencyPolicy(stmt, plan, schemas));
|
|
18042
18313
|
return plan;
|
|
18043
18314
|
}
|
|
18044
18315
|
function assertRuntimePlainGroupByPlan(stmt, groupBy, plan, subtableOwners) {
|
|
@@ -18095,7 +18366,9 @@ async function validateStatementGroupingPlanning(statement, client, cacheContext
|
|
|
18095
18366
|
}
|
|
18096
18367
|
const value = node;
|
|
18097
18368
|
if (value["type"] === "SELECT") {
|
|
18369
|
+
for (const child of Object.values(value)) await visit(child);
|
|
18098
18370
|
await validateSelectGroupingPlanning(node, client, cacheContext);
|
|
18371
|
+
return;
|
|
18099
18372
|
}
|
|
18100
18373
|
for (const child of Object.values(value)) await visit(child);
|
|
18101
18374
|
};
|
|
@@ -18143,12 +18416,12 @@ async function buildGroupingFieldResolver(stmt, client, cacheContext, materializ
|
|
|
18143
18416
|
if (field.tableAlias !== null) {
|
|
18144
18417
|
const tableIndex = tables.findIndex((table2) => effectiveTableAlias(table2) === field.tableAlias);
|
|
18145
18418
|
if (tableIndex < 0) {
|
|
18146
|
-
throw new Error(`ArgumentError:
|
|
18419
|
+
throw new Error(`ArgumentError: field ${field.tableAlias}.${field.field} has an unknown table alias.`);
|
|
18147
18420
|
}
|
|
18148
18421
|
const table = tables[tableIndex];
|
|
18149
18422
|
if (table.cteName !== null) {
|
|
18150
18423
|
throw new Error(
|
|
18151
|
-
`ArgumentError:
|
|
18424
|
+
`ArgumentError: field ${field.tableAlias}.${field.field} resolves to materialized source ${table.cteName}; physical APP fields are required.`
|
|
18152
18425
|
);
|
|
18153
18426
|
}
|
|
18154
18427
|
const code = physicalMatch(table, field.field);
|
|
@@ -18157,7 +18430,7 @@ async function buildGroupingFieldResolver(stmt, client, cacheContext, materializ
|
|
|
18157
18430
|
if (owner !== null && owner !== "") {
|
|
18158
18431
|
throw new Error(subtableGroupingAdvice(field.field, [{ appId: table.appId, owner }]));
|
|
18159
18432
|
}
|
|
18160
|
-
throw new Error(`ArgumentError:
|
|
18433
|
+
throw new Error(`ArgumentError: field ${field.tableAlias}.${field.field} does not exist in APP${table.appId}.`);
|
|
18161
18434
|
}
|
|
18162
18435
|
return resolved(table, tableIndex, field, code);
|
|
18163
18436
|
}
|
|
@@ -18168,7 +18441,7 @@ async function buildGroupingFieldResolver(stmt, client, cacheContext, materializ
|
|
|
18168
18441
|
});
|
|
18169
18442
|
const materializedMatches = tables.filter((table) => materializedHas(table, field.field));
|
|
18170
18443
|
if (physicalMatches.length + materializedMatches.length > 1) {
|
|
18171
|
-
throw new Error(`ArgumentError:
|
|
18444
|
+
throw new Error(`ArgumentError: field ${field.field} is ambiguous across multiple sources.`);
|
|
18172
18445
|
}
|
|
18173
18446
|
if (physicalMatches.length === 1 && materializedMatches.length === 0) {
|
|
18174
18447
|
const match = physicalMatches[0];
|
|
@@ -18176,7 +18449,7 @@ async function buildGroupingFieldResolver(stmt, client, cacheContext, materializ
|
|
|
18176
18449
|
}
|
|
18177
18450
|
if (materializedMatches.length === 1) {
|
|
18178
18451
|
throw new Error(
|
|
18179
|
-
`ArgumentError:
|
|
18452
|
+
`ArgumentError: field ${field.field} resolves to a materialized CTE/temp column; physical APP fields are required.`
|
|
18180
18453
|
);
|
|
18181
18454
|
}
|
|
18182
18455
|
const owners = tables.flatMap((table) => {
|
|
@@ -18188,21 +18461,34 @@ async function buildGroupingFieldResolver(stmt, client, cacheContext, materializ
|
|
|
18188
18461
|
if (uniqueOwners.length > 0) {
|
|
18189
18462
|
throw new Error(subtableGroupingAdvice(field.field, uniqueOwners));
|
|
18190
18463
|
}
|
|
18191
|
-
throw new Error(`ArgumentError:
|
|
18464
|
+
throw new Error(`ArgumentError: field ${field.field} does not exist in a physical APP source.`);
|
|
18192
18465
|
};
|
|
18193
18466
|
}
|
|
18194
18467
|
async function validateSelectGroupingPlanning(stmt, client, cacheContext, materializedTables) {
|
|
18195
18468
|
resolvedGroupingSpecs.delete(stmt);
|
|
18196
18469
|
const normalized = normalizeGroupingSpec(stmt);
|
|
18197
18470
|
const hasGroupingNodes = JSON.stringify(stmt.columns).includes('"GROUPING_') || JSON.stringify(stmt.orderBy).includes('"GROUPING_');
|
|
18198
|
-
if (normalized.type === "
|
|
18199
|
-
|
|
18200
|
-
|
|
18201
|
-
|
|
18202
|
-
|
|
18203
|
-
|
|
18471
|
+
if (normalized.type === "GROUPING_SETS" || hasGroupingNodes) {
|
|
18472
|
+
const resolver = await buildGroupingFieldResolver(stmt, client, cacheContext, materializedTables);
|
|
18473
|
+
const resolvedSpec = validateGroupingPlanning(
|
|
18474
|
+
stmt,
|
|
18475
|
+
resolver,
|
|
18476
|
+
enforceGroupingPlanningCandidateLimits
|
|
18477
|
+
);
|
|
18478
|
+
if (resolvedSpec) resolvedGroupingSpecs.set(stmt, resolvedSpec);
|
|
18479
|
+
return;
|
|
18480
|
+
}
|
|
18481
|
+
if (!isAggregateQueryBlock(stmt)) return;
|
|
18482
|
+
if (normalized.type === "NONE") {
|
|
18483
|
+
validateAggregateDependenciesStatic(stmt);
|
|
18484
|
+
return;
|
|
18485
|
+
}
|
|
18486
|
+
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
18487
|
+
const hasUnavailableMaterializedSource = sources.some(
|
|
18488
|
+
(source) => source.cteName !== null && !materializedTables?.has(source.cteName)
|
|
18204
18489
|
);
|
|
18205
|
-
if (
|
|
18490
|
+
if (hasUnavailableMaterializedSource) return;
|
|
18491
|
+
await buildRuntimePlainGroupByPlan(stmt, client, cacheContext, materializedTables);
|
|
18206
18492
|
}
|
|
18207
18493
|
function completeInputErrorPrefix(reasons) {
|
|
18208
18494
|
const reasonList = [...reasons].join(", ");
|
|
@@ -19316,7 +19602,7 @@ function mergeUnionColumnMeta(left, right) {
|
|
|
19316
19602
|
function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
19317
19603
|
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
19318
19604
|
const physicalTables = tables.filter((table) => table.cteName === null);
|
|
19319
|
-
const
|
|
19605
|
+
const outputAliases = new Set(
|
|
19320
19606
|
stmt.columns.map((column) => "alias" in column ? column.alias : null).filter((alias) => alias !== null)
|
|
19321
19607
|
);
|
|
19322
19608
|
const row = (field) => {
|
|
@@ -19340,7 +19626,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
|
|
|
19340
19626
|
return matches.length === 1 ? matches[0] : void 0;
|
|
19341
19627
|
};
|
|
19342
19628
|
const having = (field) => {
|
|
19343
|
-
if (field.tableAlias === null &&
|
|
19629
|
+
if (field.tableAlias === null && outputAliases.has(field.field)) return void 0;
|
|
19344
19630
|
return row(field);
|
|
19345
19631
|
};
|
|
19346
19632
|
return { row, having };
|
|
@@ -23359,7 +23645,7 @@ var explainJoinPushdownPlans = /* @__PURE__ */ new WeakMap();
|
|
|
23359
23645
|
var explainChoiceEqualityRewrites = /* @__PURE__ */ new WeakMap();
|
|
23360
23646
|
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
23361
23647
|
var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
|
|
23362
|
-
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4, relativeDatePlan) {
|
|
23648
|
+
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4, relativeDatePlan, initialRelations) {
|
|
23363
23649
|
const fieldApps = /* @__PURE__ */ new Set();
|
|
23364
23650
|
const processStatusApps = /* @__PURE__ */ new Set();
|
|
23365
23651
|
const numberPrecisionApps = /* @__PURE__ */ new Set();
|
|
@@ -23386,6 +23672,119 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
23386
23672
|
const relativeNodeFor = (source) => sharedRelativeDatePlan.nodes.find(
|
|
23387
23673
|
(node) => node.source === source || JSON.stringify(node.source) === JSON.stringify(source)
|
|
23388
23674
|
);
|
|
23675
|
+
const explainRelations = new Map(initialRelations ?? []);
|
|
23676
|
+
const explainSourceColumns = async (select) => {
|
|
23677
|
+
const tables = [select.from, ...select.joins.map((join2) => join2.table)];
|
|
23678
|
+
if (tables.length > 1 && select.columns.some(
|
|
23679
|
+
(column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD"
|
|
23680
|
+
)) {
|
|
23681
|
+
throw new Error(
|
|
23682
|
+
"ArgumentError: EXPLAIN could not determine the relation output schema for a multi-source wildcard SELECT."
|
|
23683
|
+
);
|
|
23684
|
+
}
|
|
23685
|
+
const columns = [];
|
|
23686
|
+
for (const table of tables) {
|
|
23687
|
+
if (table.cteName !== null) {
|
|
23688
|
+
if (table.cteName === NO_FROM_CTE_NAME) continue;
|
|
23689
|
+
const relation = explainRelations.get(table.cteName);
|
|
23690
|
+
if (!relation) {
|
|
23691
|
+
throw new Error(
|
|
23692
|
+
`ArgumentError: EXPLAIN could not determine the relation output schema for ${table.cteName}.`
|
|
23693
|
+
);
|
|
23694
|
+
}
|
|
23695
|
+
columns.push(...relation.columns);
|
|
23696
|
+
continue;
|
|
23697
|
+
}
|
|
23698
|
+
const fields = await getFieldsCached(table.appId, tracedClient, cacheContext);
|
|
23699
|
+
if (table.subtableCode) {
|
|
23700
|
+
columns.push(...fields.filter(
|
|
23701
|
+
(field) => field.inSubtable && (field.subtableCode === table.subtableCode || field.subtableCode === void 0)
|
|
23702
|
+
).map((field) => field.code));
|
|
23703
|
+
columns.push("_pid", "_rid", "_idx");
|
|
23704
|
+
columns.push(...fields.filter((field) => !field.inSubtable).map((field) => `_p.${field.code}`));
|
|
23705
|
+
} else {
|
|
23706
|
+
columns.push(...fields.filter((field) => !field.inSubtable).map((field) => field.code));
|
|
23707
|
+
columns.push(...APP_SYSTEM_FIELD_CODES);
|
|
23708
|
+
}
|
|
23709
|
+
}
|
|
23710
|
+
return [...new Set(columns)];
|
|
23711
|
+
};
|
|
23712
|
+
const inferExplainRelationColumns = async (node) => {
|
|
23713
|
+
if (node === null || typeof node !== "object") {
|
|
23714
|
+
throw new Error("ArgumentError: EXPLAIN could not determine the relation output schema.");
|
|
23715
|
+
}
|
|
23716
|
+
const typed = node;
|
|
23717
|
+
if (typed["type"] === "SELECT") {
|
|
23718
|
+
const select = node;
|
|
23719
|
+
const sourceColumns2 = await explainSourceColumns(select);
|
|
23720
|
+
if (select.columns.length === 1 && select.columns[0].type === "WILDCARD") {
|
|
23721
|
+
return sourceColumns2;
|
|
23722
|
+
}
|
|
23723
|
+
const output = [];
|
|
23724
|
+
for (const column of select.columns) {
|
|
23725
|
+
if (column.type === "WILDCARD") output.push(...sourceColumns2);
|
|
23726
|
+
else if (column.type === "PARENT_WILDCARD") {
|
|
23727
|
+
output.push(...sourceColumns2.filter((name) => name.startsWith("_p.")));
|
|
23728
|
+
} else {
|
|
23729
|
+
output.push(...project([], [column]).columns);
|
|
23730
|
+
}
|
|
23731
|
+
}
|
|
23732
|
+
return output;
|
|
23733
|
+
}
|
|
23734
|
+
if (typed["type"] === "UNION") {
|
|
23735
|
+
return inferExplainRelationColumns(typed["left"]);
|
|
23736
|
+
}
|
|
23737
|
+
if (typed["type"] === "SHOW_APPS") return [...SHOW_APPS_COLUMNS];
|
|
23738
|
+
if (typed["type"] === "DESCRIBE") return [...DESCRIBE_COLUMNS];
|
|
23739
|
+
throw new Error("ArgumentError: EXPLAIN could not determine the relation output schema.");
|
|
23740
|
+
};
|
|
23741
|
+
const preflightExplainRelations = async (node) => {
|
|
23742
|
+
if (node === null || typeof node !== "object") return;
|
|
23743
|
+
if (Array.isArray(node)) {
|
|
23744
|
+
for (const child of node) await preflightExplainRelations(child);
|
|
23745
|
+
return;
|
|
23746
|
+
}
|
|
23747
|
+
const typed = node;
|
|
23748
|
+
if (typed["type"] === "WITH") {
|
|
23749
|
+
const withStatement = node;
|
|
23750
|
+
for (const cte of withStatement.ctes) {
|
|
23751
|
+
await preflightExplainRelations(cte.query);
|
|
23752
|
+
const columns = await inferExplainRelationColumns(cte.query);
|
|
23753
|
+
explainRelations.set(cte.name, { rows: [], columns });
|
|
23754
|
+
}
|
|
23755
|
+
await preflightExplainRelations(withStatement.query);
|
|
23756
|
+
return;
|
|
23757
|
+
}
|
|
23758
|
+
if (typed["type"] === "UNION") {
|
|
23759
|
+
await preflightExplainRelations(typed["left"]);
|
|
23760
|
+
await preflightExplainRelations(typed["right"]);
|
|
23761
|
+
return;
|
|
23762
|
+
}
|
|
23763
|
+
if (typed["type"] === "SELECT") {
|
|
23764
|
+
const select = node;
|
|
23765
|
+
for (const column of select.columns) {
|
|
23766
|
+
if (column.type === "SCALAR_SUBQUERY_COL") await preflightExplainRelations(column.query);
|
|
23767
|
+
}
|
|
23768
|
+
await preflightExplainRelations(select.where);
|
|
23769
|
+
await preflightExplainRelations(select.having);
|
|
23770
|
+
await validateSelectGroupingPlanning(
|
|
23771
|
+
select,
|
|
23772
|
+
tracedClient,
|
|
23773
|
+
cacheContext,
|
|
23774
|
+
explainRelations
|
|
23775
|
+
);
|
|
23776
|
+
const plainPlan = await buildRuntimePlainGroupByPlan(
|
|
23777
|
+
select,
|
|
23778
|
+
tracedClient,
|
|
23779
|
+
cacheContext,
|
|
23780
|
+
explainRelations
|
|
23781
|
+
);
|
|
23782
|
+
if (plainPlan) plainGroupByPlans.set(select, plainPlan);
|
|
23783
|
+
return;
|
|
23784
|
+
}
|
|
23785
|
+
for (const child of Object.values(typed)) await preflightExplainRelations(child);
|
|
23786
|
+
};
|
|
23787
|
+
await preflightExplainRelations(query);
|
|
23389
23788
|
const visit = async (node) => {
|
|
23390
23789
|
if (node === null || typeof node !== "object") return;
|
|
23391
23790
|
if (seen.has(node)) return;
|
|
@@ -24057,6 +24456,7 @@ function buildPlanForBatchQuery(query, info, capabilities, orderPlans, collector
|
|
|
24057
24456
|
lines.push(" note: \u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3078\u306E WHERE \u30D7\u30C3\u30B7\u30E5\u30C0\u30A6\u30F3\u306F\u884C\u308F\u308C\u306A\u3044");
|
|
24058
24457
|
return lines;
|
|
24059
24458
|
}
|
|
24459
|
+
var explainMaterializedTables = /* @__PURE__ */ new WeakMap();
|
|
24060
24460
|
async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxActive, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS, relativeDatePlan) {
|
|
24061
24461
|
const sharedPlan = relativeDatePlan ?? await resolveRelativeDateExecutionPlan(stmt.query, client, cacheContext);
|
|
24062
24462
|
const analysis = await buildExplainWhereAnalysis(
|
|
@@ -24064,7 +24464,8 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
|
|
|
24064
24464
|
client,
|
|
24065
24465
|
cacheContext,
|
|
24066
24466
|
maxRecords,
|
|
24067
|
-
sharedPlan
|
|
24467
|
+
sharedPlan,
|
|
24468
|
+
explainMaterializedTables.get(stmt)
|
|
24068
24469
|
);
|
|
24069
24470
|
const fetchCollector = { sources: [] };
|
|
24070
24471
|
const relativeLines = relativeDateExplainLines(sharedPlan);
|
|
@@ -24358,6 +24759,12 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
24358
24759
|
lines.push(
|
|
24359
24760
|
` group key ${key.name}: PHYSICAL (source=${item.sourceIndex}, field=${item.fieldCode})`
|
|
24360
24761
|
);
|
|
24762
|
+
} else if (item.kind === "ALIAS_SAFE") {
|
|
24763
|
+
lines.push(
|
|
24764
|
+
` group key ${key.name}: ALIAS_SAFE (column=${item.columnIndex})`
|
|
24765
|
+
);
|
|
24766
|
+
} else if (item.kind === "EXPRESSION") {
|
|
24767
|
+
lines.push(` group key ${key.name}: EXPRESSION`);
|
|
24361
24768
|
}
|
|
24362
24769
|
});
|
|
24363
24770
|
} else if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) {
|