@rex0220/kintone-sql-tools 3.25.0 → 3.26.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 +2322 -1892
- package/dist-engine/index.cjs +7 -7
- package/dist-engine/index.mjs +7 -7
- package/dist-engine/ksql-engine.umd.js +7 -7
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +56 -17
- package/dist-engine/meta/esm.json +56 -17
- package/dist-engine/meta/umd.json +56 -17
- package/dist-mcp/ksql-mcp.js +916 -483
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +4 -3
package/dist-cli/ksql.js
CHANGED
|
@@ -10699,2088 +10699,2421 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
|
|
|
10699
10699
|
};
|
|
10700
10700
|
}
|
|
10701
10701
|
|
|
10702
|
-
// src/core/optimization/
|
|
10703
|
-
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10702
|
+
// src/core/optimization/whereCapability.ts
|
|
10703
|
+
var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
|
|
10704
|
+
var RELATIVE_DATE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
10705
|
+
"DATE",
|
|
10706
|
+
"DATETIME",
|
|
10707
|
+
"CREATED_TIME",
|
|
10708
|
+
"UPDATED_TIME"
|
|
10709
|
+
]);
|
|
10710
|
+
var RELATIVE_DATE_OPERATORS = new Set(RANGE_AND_EQUALITY);
|
|
10711
|
+
var LEGACY_KINTONE_FUNCTION_FIELD_TYPES = /* @__PURE__ */ new Map([
|
|
10712
|
+
["TODAY", /* @__PURE__ */ new Set(["DATE", "DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
10713
|
+
["NOW", /* @__PURE__ */ new Set(["DATETIME", "CREATED_TIME", "UPDATED_TIME"])],
|
|
10714
|
+
["LOGINUSER", /* @__PURE__ */ new Set(["CREATOR", "MODIFIER", "USER_SELECT"])]
|
|
10715
|
+
]);
|
|
10716
|
+
var LEGACY_KINTONE_FUNCTION_OPERATORS = /* @__PURE__ */ new Map([
|
|
10717
|
+
["TODAY", new Set(RANGE_AND_EQUALITY)],
|
|
10718
|
+
["NOW", new Set(RANGE_AND_EQUALITY)],
|
|
10719
|
+
["LOGINUSER", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
10720
|
+
]);
|
|
10721
|
+
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
10722
|
+
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
10723
|
+
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
10724
|
+
["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
10725
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10726
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10727
|
+
["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
10728
|
+
["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
|
|
10729
|
+
["DATE", new Set(RANGE_AND_EQUALITY)],
|
|
10730
|
+
["TIME", new Set(RANGE_AND_EQUALITY)],
|
|
10731
|
+
["DATETIME", new Set(RANGE_AND_EQUALITY)],
|
|
10732
|
+
["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
10733
|
+
["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
|
|
10734
|
+
["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
10735
|
+
["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
10736
|
+
["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
10737
|
+
["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
10738
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10739
|
+
["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10740
|
+
["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10741
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10742
|
+
["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
|
|
10743
|
+
["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10744
|
+
["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10745
|
+
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10746
|
+
["STATUS", new Set(EQUALITY_IN)]
|
|
10747
|
+
]);
|
|
10748
|
+
var LOCAL_VALID_OPERATORS = /* @__PURE__ */ new Map([
|
|
10749
|
+
["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10750
|
+
["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10751
|
+
["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
10752
|
+
["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])]
|
|
10753
|
+
]);
|
|
10754
|
+
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
10755
|
+
"RECORD_NUMBER",
|
|
10756
|
+
"__ID__",
|
|
10757
|
+
"CREATOR",
|
|
10758
|
+
"MODIFIER",
|
|
10759
|
+
"CREATED_TIME",
|
|
10760
|
+
"UPDATED_TIME",
|
|
10761
|
+
"DATE",
|
|
10762
|
+
"TIME",
|
|
10763
|
+
"DATETIME",
|
|
10764
|
+
"SINGLE_LINE_TEXT",
|
|
10765
|
+
"LINK",
|
|
10766
|
+
"NUMBER",
|
|
10767
|
+
"CALC",
|
|
10768
|
+
"MULTI_LINE_TEXT",
|
|
10769
|
+
"RICH_TEXT",
|
|
10770
|
+
"RADIO_BUTTON",
|
|
10771
|
+
"DROP_DOWN",
|
|
10772
|
+
"STATUS",
|
|
10773
|
+
// 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
|
|
10774
|
+
"KSQL_STRING",
|
|
10775
|
+
"KSQL_NUMBER",
|
|
10776
|
+
"KSQL_BOOLEAN"
|
|
10777
|
+
]);
|
|
10778
|
+
var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
|
|
10779
|
+
"CHECK_BOX",
|
|
10780
|
+
"MULTI_SELECT",
|
|
10781
|
+
"FILE",
|
|
10782
|
+
"USER_SELECT",
|
|
10783
|
+
"ORGANIZATION_SELECT",
|
|
10784
|
+
"GROUP_SELECT",
|
|
10785
|
+
"STATUS_ASSIGNEE",
|
|
10786
|
+
"CATEGORY"
|
|
10787
|
+
]);
|
|
10788
|
+
function nativeWhereOperatorsForType(fieldType) {
|
|
10789
|
+
return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
|
|
10722
10790
|
}
|
|
10723
|
-
function
|
|
10724
|
-
|
|
10791
|
+
function classifyWhereCapability(where, resolveField2) {
|
|
10792
|
+
if (where === null) {
|
|
10793
|
+
return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
|
|
10794
|
+
}
|
|
10795
|
+
return classifyNode(where, resolveField2);
|
|
10725
10796
|
}
|
|
10726
|
-
function
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10797
|
+
function classifyNode(where, resolveField2) {
|
|
10798
|
+
switch (where.type) {
|
|
10799
|
+
case "BOOLEAN":
|
|
10800
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
10801
|
+
case "BINARY":
|
|
10802
|
+
return classifyBinary(where.op, where.left, where.right, resolveField2);
|
|
10803
|
+
case "NULL_CHECK":
|
|
10804
|
+
if (where.field.type !== "FIELD") return localExpression();
|
|
10805
|
+
return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
|
|
10806
|
+
case "EXISTS":
|
|
10807
|
+
return localExpression();
|
|
10808
|
+
case "GROUP":
|
|
10809
|
+
return classifyNode(where.expr, resolveField2);
|
|
10810
|
+
case "NOT": {
|
|
10811
|
+
const inner = classifyNode(where.expr, resolveField2);
|
|
10812
|
+
if (inner.capability !== "SUPERSET_PREFILTER") return inner;
|
|
10813
|
+
if (!hasRelativeDateReason(inner.reasons)) {
|
|
10814
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
10815
|
+
}
|
|
10816
|
+
return requireExactFunctionPushdown({
|
|
10817
|
+
capability: "LOCAL_ONLY",
|
|
10818
|
+
reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }, ...inner.reasons]
|
|
10819
|
+
});
|
|
10820
|
+
}
|
|
10821
|
+
case "LOGICAL": {
|
|
10822
|
+
const left = classifyNode(where.left, resolveField2);
|
|
10823
|
+
const right = classifyNode(where.right, resolveField2);
|
|
10824
|
+
return combineLogical(where.op, left, right);
|
|
10739
10825
|
}
|
|
10740
|
-
if (semantics.fieldType === "KSQL_AMBIGUOUS") reasons.push("ORDER_KEY_AMBIGUOUS");
|
|
10741
|
-
else if (semantics.compareMode === "unsupported") reasons.push("ORDER_KEY_UNSUPPORTED");
|
|
10742
10826
|
}
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10827
|
+
}
|
|
10828
|
+
function classifyBinary(op, left, right, resolveField2) {
|
|
10829
|
+
if (right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(right.name)) {
|
|
10830
|
+
return classifyRelativeDateBinary(op, left, right, resolveField2);
|
|
10831
|
+
}
|
|
10832
|
+
if (right.type === "KINTONE_FUNC" && isLegacyKintoneFunction(right)) {
|
|
10833
|
+
return classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2);
|
|
10834
|
+
}
|
|
10835
|
+
if (right.type === "IN_LIST") {
|
|
10836
|
+
const functions = right.values.filter(
|
|
10837
|
+
(value) => value.type === "KINTONE_FUNC"
|
|
10746
10838
|
);
|
|
10839
|
+
if (functions.length > 0) {
|
|
10840
|
+
if (right.values.length === 1 && functions.length === 1) {
|
|
10841
|
+
return classifyLegacyKintoneFunctionBinary(op, left, functions[0], resolveField2);
|
|
10842
|
+
}
|
|
10843
|
+
return legacyKintoneFunctionUnsupported(
|
|
10844
|
+
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
10845
|
+
functions[0].name,
|
|
10846
|
+
left.type === "FIELD" ? left.field : void 0,
|
|
10847
|
+
left.type === "FIELD" ? resolveField2(left)?.fieldType : void 0,
|
|
10848
|
+
normalizeOperator(op)
|
|
10849
|
+
);
|
|
10850
|
+
}
|
|
10747
10851
|
}
|
|
10748
|
-
if (
|
|
10749
|
-
|
|
10750
|
-
|
|
10852
|
+
if (left.type !== "FIELD") return localExpression();
|
|
10853
|
+
const semantics = resolveField2(left);
|
|
10854
|
+
if (!semantics) {
|
|
10855
|
+
return unsupported2("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
|
|
10751
10856
|
}
|
|
10752
|
-
const
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10857
|
+
const nativeOp = normalizeOperator(op);
|
|
10858
|
+
if (!isLocallyValidOperator(semantics.fieldType, nativeOp)) {
|
|
10859
|
+
return unsupported2(
|
|
10860
|
+
"WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE",
|
|
10861
|
+
left.field,
|
|
10862
|
+
semantics.fieldType,
|
|
10863
|
+
nativeOp
|
|
10864
|
+
);
|
|
10760
10865
|
}
|
|
10761
|
-
if ((
|
|
10762
|
-
|
|
10866
|
+
if (!hasLocalContract(semantics.fieldType, op)) {
|
|
10867
|
+
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, nativeOp);
|
|
10763
10868
|
}
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
|
|
10869
|
+
const native = nativeWhereOperatorsForType(semantics.fieldType);
|
|
10870
|
+
const rightCanPush = right.type === "STRING" || right.type === "NUMBER" || right.type === "IN_LIST" || isLegacyKintoneFunction(right);
|
|
10871
|
+
const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
|
|
10872
|
+
const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
|
|
10873
|
+
if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
|
|
10767
10874
|
return {
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
|
|
10772
|
-
|
|
10875
|
+
capability: "EXACT_PUSHDOWN",
|
|
10876
|
+
reasons: [{
|
|
10877
|
+
code: "WHERE_EXACT",
|
|
10878
|
+
field: left.field,
|
|
10879
|
+
fieldType: semantics.fieldType,
|
|
10880
|
+
operator: nativeOp
|
|
10881
|
+
}]
|
|
10773
10882
|
};
|
|
10774
10883
|
}
|
|
10775
10884
|
return {
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10885
|
+
capability: "LOCAL_ONLY",
|
|
10886
|
+
reasons: [{
|
|
10887
|
+
code: "WHERE_RESIDUAL",
|
|
10888
|
+
field: left.field,
|
|
10889
|
+
fieldType: semantics.fieldType,
|
|
10890
|
+
operator: nativeOp
|
|
10891
|
+
}]
|
|
10781
10892
|
};
|
|
10782
10893
|
}
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
var KORDER_NATIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
10786
|
-
"RECORD_NUMBER",
|
|
10787
|
-
"SINGLE_LINE_TEXT",
|
|
10788
|
-
"NUMBER",
|
|
10789
|
-
"CALC",
|
|
10790
|
-
"DATE",
|
|
10791
|
-
"DATETIME",
|
|
10792
|
-
"TIME",
|
|
10793
|
-
"CREATED_TIME",
|
|
10794
|
-
"UPDATED_TIME",
|
|
10795
|
-
"DROP_DOWN",
|
|
10796
|
-
"RADIO_BUTTON",
|
|
10797
|
-
"STATUS",
|
|
10798
|
-
"LINK",
|
|
10799
|
-
"CREATOR",
|
|
10800
|
-
"MODIFIER"
|
|
10801
|
-
]);
|
|
10802
|
-
function hasSelectOutputAlias2(stmt, name) {
|
|
10803
|
-
return stmt.columns.some((column) => "alias" in column && column.alias === name);
|
|
10894
|
+
function isLegacyKintoneFunction(value) {
|
|
10895
|
+
return value.type === "KINTONE_FUNC" && LEGACY_KINTONE_FUNCTION_NAMES.has(value.name);
|
|
10804
10896
|
}
|
|
10805
|
-
function
|
|
10806
|
-
const
|
|
10807
|
-
const
|
|
10808
|
-
if (
|
|
10809
|
-
|
|
10810
|
-
|
|
10897
|
+
function classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2) {
|
|
10898
|
+
const operator = normalizeOperator(op);
|
|
10899
|
+
const functionName = right.name;
|
|
10900
|
+
if (!LEGACY_KINTONE_FUNCTION_NAMES.has(functionName) || left.type !== "FIELD") {
|
|
10901
|
+
return legacyKintoneFunctionUnsupported(
|
|
10902
|
+
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
10903
|
+
functionName,
|
|
10904
|
+
void 0,
|
|
10905
|
+
void 0,
|
|
10906
|
+
operator
|
|
10907
|
+
);
|
|
10811
10908
|
}
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10909
|
+
const semantics = resolveField2(left);
|
|
10910
|
+
const validFieldTypes = LEGACY_KINTONE_FUNCTION_FIELD_TYPES.get(functionName);
|
|
10911
|
+
if (!semantics || !validFieldTypes.has(semantics.fieldType) || semantics.inSubtable || semantics.requiresCollectionOperators) {
|
|
10912
|
+
return legacyKintoneFunctionUnsupported(
|
|
10913
|
+
"WHERE_KINTONE_FUNCTION_FIELD_TYPE_UNSUPPORTED",
|
|
10914
|
+
functionName,
|
|
10915
|
+
left.field,
|
|
10916
|
+
semantics?.fieldType,
|
|
10917
|
+
operator
|
|
10918
|
+
);
|
|
10815
10919
|
}
|
|
10816
|
-
|
|
10817
|
-
if (
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
if (!semantics) {
|
|
10826
|
-
reasons.push(`KORDER_KEY_UNRESOLVED(field=${name})`);
|
|
10827
|
-
continue;
|
|
10828
|
-
}
|
|
10829
|
-
if (hasSelectOutputAlias2(stmt, name)) {
|
|
10830
|
-
reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(field=${name})`);
|
|
10831
|
-
continue;
|
|
10832
|
-
}
|
|
10833
|
-
if (name === "$id") continue;
|
|
10834
|
-
if (!semantics.source || semantics.source.fieldCode !== name) {
|
|
10835
|
-
reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(field=${name})`);
|
|
10836
|
-
continue;
|
|
10837
|
-
}
|
|
10838
|
-
if (!KORDER_NATIVE_FIELD_TYPES.has(semantics.fieldType)) {
|
|
10839
|
-
reasons.push(`KORDER_TYPE_UNSUPPORTED(field=${name}, type=${semantics.fieldType})`);
|
|
10840
|
-
}
|
|
10920
|
+
const validOperators = LEGACY_KINTONE_FUNCTION_OPERATORS.get(functionName);
|
|
10921
|
+
if (!validOperators.has(operator)) {
|
|
10922
|
+
return legacyKintoneFunctionUnsupported(
|
|
10923
|
+
"WHERE_KINTONE_FUNCTION_OPERATOR_UNSUPPORTED",
|
|
10924
|
+
functionName,
|
|
10925
|
+
left.field,
|
|
10926
|
+
semantics.fieldType,
|
|
10927
|
+
operator
|
|
10928
|
+
);
|
|
10841
10929
|
}
|
|
10842
|
-
|
|
10843
|
-
|
|
10930
|
+
return {
|
|
10931
|
+
capability: "EXACT_PUSHDOWN",
|
|
10932
|
+
reasons: [{
|
|
10933
|
+
code: "WHERE_EXACT",
|
|
10934
|
+
functionName,
|
|
10935
|
+
field: left.field,
|
|
10936
|
+
fieldType: semantics.fieldType,
|
|
10937
|
+
operator
|
|
10938
|
+
}]
|
|
10939
|
+
};
|
|
10940
|
+
}
|
|
10941
|
+
function classifyRelativeDateBinary(op, left, right, resolveField2) {
|
|
10942
|
+
const operator = normalizeOperator(op);
|
|
10943
|
+
const functionName = right.name;
|
|
10944
|
+
if (left.type !== "FIELD") {
|
|
10945
|
+
return relativeDateUnsupported(
|
|
10946
|
+
"WHERE_RELATIVE_DATE_CONTEXT_UNSUPPORTED",
|
|
10947
|
+
functionName,
|
|
10948
|
+
void 0,
|
|
10949
|
+
void 0,
|
|
10950
|
+
operator
|
|
10951
|
+
);
|
|
10844
10952
|
}
|
|
10845
|
-
const
|
|
10846
|
-
if (!
|
|
10847
|
-
|
|
10953
|
+
const semantics = resolveField2(left);
|
|
10954
|
+
if (!semantics) {
|
|
10955
|
+
return relativeDateUnsupported(
|
|
10956
|
+
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
10957
|
+
functionName,
|
|
10958
|
+
left.field,
|
|
10959
|
+
void 0,
|
|
10960
|
+
operator
|
|
10961
|
+
);
|
|
10848
10962
|
}
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10963
|
+
if (!hasValidRelativeDateArguments(right)) {
|
|
10964
|
+
return relativeDateUnsupported(
|
|
10965
|
+
"WHERE_RELATIVE_DATE_ARGUMENT_INVALID",
|
|
10966
|
+
functionName,
|
|
10967
|
+
left.field,
|
|
10968
|
+
semantics.fieldType,
|
|
10969
|
+
operator
|
|
10970
|
+
);
|
|
10852
10971
|
}
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10972
|
+
if (!RELATIVE_DATE_OPERATORS.has(operator)) {
|
|
10973
|
+
return relativeDateUnsupported(
|
|
10974
|
+
"WHERE_RELATIVE_DATE_OPERATOR_UNSUPPORTED",
|
|
10975
|
+
functionName,
|
|
10976
|
+
left.field,
|
|
10977
|
+
semantics.fieldType,
|
|
10978
|
+
operator
|
|
10857
10979
|
);
|
|
10858
10980
|
}
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10981
|
+
if (!RELATIVE_DATE_FIELD_TYPES.has(semantics.fieldType) || semantics.inSubtable || semantics.requiresCollectionOperators) {
|
|
10982
|
+
return relativeDateUnsupported(
|
|
10983
|
+
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
10984
|
+
functionName,
|
|
10985
|
+
left.field,
|
|
10986
|
+
semantics.fieldType,
|
|
10987
|
+
operator
|
|
10863
10988
|
);
|
|
10864
10989
|
}
|
|
10865
10990
|
return {
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10991
|
+
capability: "EXACT_PUSHDOWN",
|
|
10992
|
+
reasons: [{
|
|
10993
|
+
code: "WHERE_EXACT",
|
|
10994
|
+
functionName,
|
|
10995
|
+
field: left.field,
|
|
10996
|
+
fieldType: semantics.fieldType,
|
|
10997
|
+
operator
|
|
10998
|
+
}]
|
|
10872
10999
|
};
|
|
10873
11000
|
}
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
|
|
10885
|
-
|
|
10886
|
-
|
|
11001
|
+
function hasValidRelativeDateArguments(value) {
|
|
11002
|
+
if (!("args" in value) || !value.args) return false;
|
|
11003
|
+
switch (value.name) {
|
|
11004
|
+
case "YESTERDAY":
|
|
11005
|
+
case "TOMORROW":
|
|
11006
|
+
case "THIS_YEAR":
|
|
11007
|
+
case "LAST_YEAR":
|
|
11008
|
+
case "NEXT_YEAR":
|
|
11009
|
+
return value.args.kind === "NONE";
|
|
11010
|
+
case "FROM_TODAY":
|
|
11011
|
+
return value.args.kind === "FROM_TODAY" && Number.isSafeInteger(value.args.offset) && value.args.offsetText === String(value.args.offset === 0 ? 0 : value.args.offset) && (value.args.unit === "DAYS" || value.args.unit === "WEEKS" || value.args.unit === "MONTHS" || value.args.unit === "YEARS");
|
|
11012
|
+
case "THIS_WEEK":
|
|
11013
|
+
case "LAST_WEEK":
|
|
11014
|
+
case "NEXT_WEEK":
|
|
11015
|
+
return value.args.kind === "WEEK" && (value.args.weekday === null || value.args.weekday === "SUNDAY" || value.args.weekday === "MONDAY" || value.args.weekday === "TUESDAY" || value.args.weekday === "WEDNESDAY" || value.args.weekday === "THURSDAY" || value.args.weekday === "FRIDAY" || value.args.weekday === "SATURDAY");
|
|
11016
|
+
case "THIS_MONTH":
|
|
11017
|
+
case "LAST_MONTH":
|
|
11018
|
+
case "NEXT_MONTH":
|
|
11019
|
+
return value.args.kind === "MONTH" && (value.args.day === null || value.args.day === "LAST" || Number.isInteger(value.args.day) && value.args.day >= 1 && value.args.day <= 31);
|
|
11020
|
+
default:
|
|
11021
|
+
return false;
|
|
10887
11022
|
}
|
|
10888
|
-
}
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
11023
|
+
}
|
|
11024
|
+
function classifyLocalOnlyField(field, operator, resolveField2) {
|
|
11025
|
+
const semantics = resolveField2(field);
|
|
11026
|
+
if (!semantics) return unsupported2("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
|
|
11027
|
+
if (!isLocallyValidOperator(semantics.fieldType, operator)) {
|
|
11028
|
+
return unsupported2(
|
|
11029
|
+
"WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE",
|
|
11030
|
+
field.field,
|
|
11031
|
+
semantics.fieldType,
|
|
11032
|
+
operator
|
|
11033
|
+
);
|
|
10895
11034
|
}
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
// src/core/optimization/korderCursorExecutor.ts
|
|
10899
|
-
async function executeKorderCursor(input) {
|
|
10900
|
-
const handle = await input.client.openCursor({
|
|
10901
|
-
app: input.app,
|
|
10902
|
-
fields: input.fields.length > 0 ? input.fields : void 0,
|
|
10903
|
-
query: input.query,
|
|
10904
|
-
size: 500
|
|
10905
|
-
});
|
|
10906
|
-
const records = [];
|
|
10907
|
-
let seen = 0;
|
|
10908
|
-
let primaryError;
|
|
10909
|
-
let cleanupWarning;
|
|
10910
|
-
try {
|
|
10911
|
-
if (handle.totalCount > input.offset) {
|
|
10912
|
-
while (records.length < input.limit) {
|
|
10913
|
-
const page = await handle.nextPage();
|
|
10914
|
-
for (const record of page.records) {
|
|
10915
|
-
if (seen < input.offset) seen += 1;
|
|
10916
|
-
else if (records.length < input.limit) records.push(record);
|
|
10917
|
-
else break;
|
|
10918
|
-
}
|
|
10919
|
-
if (!page.next) break;
|
|
10920
|
-
}
|
|
10921
|
-
}
|
|
10922
|
-
} catch (error) {
|
|
10923
|
-
primaryError = error;
|
|
10924
|
-
throw error;
|
|
10925
|
-
} finally {
|
|
10926
|
-
try {
|
|
10927
|
-
await handle.close();
|
|
10928
|
-
} catch (cleanupError) {
|
|
10929
|
-
if (primaryError && primaryError instanceof Error) {
|
|
10930
|
-
Object.defineProperty(primaryError, "cursorCleanupError", {
|
|
10931
|
-
value: cleanupError,
|
|
10932
|
-
configurable: true
|
|
10933
|
-
});
|
|
10934
|
-
} else {
|
|
10935
|
-
cleanupWarning = new CursorCleanupWarning(cleanupError).message;
|
|
10936
|
-
}
|
|
10937
|
-
}
|
|
11035
|
+
if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
|
|
11036
|
+
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
|
|
10938
11037
|
}
|
|
10939
|
-
return {
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
function buildKorderCursorQuery(stmt) {
|
|
10944
|
-
const parts = [];
|
|
10945
|
-
if (stmt.where) parts.push(whereToKintone(stmt.where));
|
|
10946
|
-
const order = stmt.orderBy.map((item) => {
|
|
10947
|
-
if (item.key.type !== "FIELD_NAME") {
|
|
10948
|
-
throw new Error("ArgumentError: KORDER cursor key must be a direct field.");
|
|
10949
|
-
}
|
|
10950
|
-
return `${item.key.name} ${item.direction === "ASC" ? "asc" : "desc"}`;
|
|
10951
|
-
});
|
|
10952
|
-
parts.push(`order by ${order.join(", ")}`);
|
|
10953
|
-
return parts.join(" ");
|
|
11038
|
+
return {
|
|
11039
|
+
capability: "LOCAL_ONLY",
|
|
11040
|
+
reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
|
|
11041
|
+
};
|
|
10954
11042
|
}
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
"$id",
|
|
10959
|
-
"$revision",
|
|
10960
|
-
"\u30EC\u30B3\u30FC\u30C9\u756A\u53F7",
|
|
10961
|
-
"\u4F5C\u6210\u8005",
|
|
10962
|
-
"\u4F5C\u6210\u65E5\u6642",
|
|
10963
|
-
"\u66F4\u65B0\u8005",
|
|
10964
|
-
"\u66F4\u65B0\u65E5\u6642",
|
|
10965
|
-
"\u30B9\u30C6\u30FC\u30BF\u30B9",
|
|
10966
|
-
"\u4F5C\u696D\u8005"
|
|
10967
|
-
];
|
|
10968
|
-
function isSystemLikeFieldCode(code) {
|
|
10969
|
-
return code.startsWith("_") || code.startsWith("$");
|
|
11043
|
+
function isLocallyValidOperator(fieldType, operator) {
|
|
11044
|
+
const policy = LOCAL_VALID_OPERATORS.get(fieldType);
|
|
11045
|
+
return policy === void 0 || policy.has(operator);
|
|
10970
11046
|
}
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
11047
|
+
function hasLocalContract(fieldType, op) {
|
|
11048
|
+
if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
|
|
11049
|
+
if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
|
|
11050
|
+
return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
|
|
11051
|
+
}
|
|
11052
|
+
function normalizeOperator(op) {
|
|
11053
|
+
switch (op) {
|
|
11054
|
+
case "<>":
|
|
11055
|
+
return "!=";
|
|
11056
|
+
case "IN":
|
|
11057
|
+
return "in";
|
|
11058
|
+
case "NOT_IN":
|
|
11059
|
+
return "not in";
|
|
11060
|
+
case "LIKE":
|
|
11061
|
+
case "KLIKE":
|
|
11062
|
+
return "like";
|
|
11063
|
+
case "NOT_LIKE":
|
|
11064
|
+
case "NOT_KLIKE":
|
|
11065
|
+
return "not like";
|
|
11066
|
+
default:
|
|
11067
|
+
return op;
|
|
10982
11068
|
}
|
|
10983
|
-
return Object.values(value).some(containsAggregateColumnNode);
|
|
10984
11069
|
}
|
|
10985
|
-
function
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10998
|
-
case "LITERAL_COL":
|
|
10999
|
-
case "ARITH_COL":
|
|
11000
|
-
case "CASE_COL":
|
|
11001
|
-
case "STRFUNC_COL":
|
|
11002
|
-
case "SCALAR_VALUE_COL":
|
|
11003
|
-
case "SCALAR_SUBQUERY_COL":
|
|
11004
|
-
return containsAggregate(column) || containsAggregateColumnNode(column) ? "AGGREGATE_DEPENDENT" : "SAFE";
|
|
11070
|
+
function combineLogical(op, left, right) {
|
|
11071
|
+
const reasons = [...left.reasons, ...right.reasons];
|
|
11072
|
+
if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
|
|
11073
|
+
return requireExactFunctionPushdown({ capability: "UNSUPPORTED", reasons });
|
|
11074
|
+
}
|
|
11075
|
+
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
11076
|
+
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
11077
|
+
}
|
|
11078
|
+
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
11079
|
+
return requireExactFunctionPushdown({
|
|
11080
|
+
capability: "SUPERSET_PREFILTER",
|
|
11081
|
+
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
11082
|
+
});
|
|
11005
11083
|
}
|
|
11084
|
+
return requireExactFunctionPushdown({ capability: "LOCAL_ONLY", reasons });
|
|
11006
11085
|
}
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
return [...new Set(values)];
|
|
11086
|
+
function localExpression() {
|
|
11087
|
+
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
11010
11088
|
}
|
|
11011
|
-
function
|
|
11012
|
-
|
|
11013
|
-
case "APP":
|
|
11014
|
-
return unique([
|
|
11015
|
-
...input.fieldCodes,
|
|
11016
|
-
...APP_SYSTEM_FIELD_CODES
|
|
11017
|
-
]);
|
|
11018
|
-
case "SUBTABLE":
|
|
11019
|
-
return unique([
|
|
11020
|
-
...input.childFieldCodes,
|
|
11021
|
-
...SUBTABLE_SYSTEM_COLUMNS,
|
|
11022
|
-
...input.parentFieldCodes.map((field) => `_p.${field}`)
|
|
11023
|
-
]);
|
|
11024
|
-
case "MATERIALIZED":
|
|
11025
|
-
return unique(input.columns);
|
|
11026
|
-
}
|
|
11089
|
+
function unsupported2(code, field, fieldType, operator) {
|
|
11090
|
+
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
11027
11091
|
}
|
|
11028
|
-
function
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
columns: sourceColumns(lookup(source, sourceIndex))
|
|
11034
|
-
}));
|
|
11092
|
+
function legacyKintoneFunctionUnsupported(code, functionName, field, fieldType, operator) {
|
|
11093
|
+
return requireExactFunctionPushdown({
|
|
11094
|
+
capability: "UNSUPPORTED",
|
|
11095
|
+
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
11096
|
+
});
|
|
11035
11097
|
}
|
|
11036
|
-
function
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
}
|
|
11042
|
-
return { qualifier: name.slice(0, dot), fieldCode: name.slice(dot + 1) };
|
|
11098
|
+
function relativeDateUnsupported(code, functionName, field, fieldType, operator) {
|
|
11099
|
+
return requireExactRelativeDatePushdown({
|
|
11100
|
+
capability: "UNSUPPORTED",
|
|
11101
|
+
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
11102
|
+
});
|
|
11043
11103
|
}
|
|
11044
|
-
function
|
|
11045
|
-
return
|
|
11104
|
+
function hasRelativeDateReason(reasons) {
|
|
11105
|
+
return reasons.some(
|
|
11106
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
11107
|
+
);
|
|
11046
11108
|
}
|
|
11047
|
-
function
|
|
11048
|
-
return
|
|
11109
|
+
function hasLegacyKintoneFunctionReason(reasons) {
|
|
11110
|
+
return reasons.some(
|
|
11111
|
+
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
11112
|
+
);
|
|
11049
11113
|
}
|
|
11050
|
-
function
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
const physical = candidateSources.filter((source) => source.columns.includes(parsed.fieldCode));
|
|
11054
|
-
if (physical.length > 1) {
|
|
11055
|
-
throw new Error(
|
|
11056
|
-
`ArgumentError: GROUP BY field ${name} is ambiguous across multiple sources (reason=GROUP_BY_FIELD_AMBIGUOUS).`
|
|
11057
|
-
);
|
|
11058
|
-
}
|
|
11059
|
-
if (physical.length === 1) {
|
|
11060
|
-
const source = physical[0];
|
|
11061
|
-
return {
|
|
11062
|
-
kind: "PHYSICAL",
|
|
11063
|
-
sourceIndex: source.sourceIndex,
|
|
11064
|
-
fieldCode: parsed.fieldCode,
|
|
11065
|
-
runtimeKey: runtimeKey(source, parsed.fieldCode)
|
|
11066
|
-
};
|
|
11114
|
+
function requireExactRelativeDatePushdown(result) {
|
|
11115
|
+
if (result.capability === "EXACT_PUSHDOWN" || !hasRelativeDateReason(result.reasons) || result.reasons.some((reason) => reason.code === "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN")) {
|
|
11116
|
+
return result;
|
|
11067
11117
|
}
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
(column, columnIndex) => explicitAlias(column) === name ? [{ column, columnIndex }] : []
|
|
11118
|
+
const relative = result.reasons.find(
|
|
11119
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
11071
11120
|
);
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11121
|
+
return {
|
|
11122
|
+
capability: result.capability,
|
|
11123
|
+
reasons: [
|
|
11124
|
+
...result.reasons,
|
|
11125
|
+
{
|
|
11126
|
+
code: "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN",
|
|
11127
|
+
functionName: relative.functionName,
|
|
11128
|
+
field: relative.field,
|
|
11129
|
+
fieldType: relative.fieldType,
|
|
11130
|
+
operator: relative.operator
|
|
11131
|
+
}
|
|
11132
|
+
]
|
|
11133
|
+
};
|
|
11134
|
+
}
|
|
11135
|
+
function requireExactLegacyKintoneFunctionPushdown(result) {
|
|
11136
|
+
if (result.capability === "EXACT_PUSHDOWN" || !hasLegacyKintoneFunctionReason(result.reasons) || result.reasons.some(
|
|
11137
|
+
(reason) => reason.code === "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN"
|
|
11138
|
+
)) {
|
|
11139
|
+
return result;
|
|
11083
11140
|
}
|
|
11084
|
-
const
|
|
11085
|
-
(
|
|
11141
|
+
const legacy = result.reasons.find(
|
|
11142
|
+
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
11086
11143
|
);
|
|
11087
|
-
if (aggregateSyntheticMatch) return { kind: "ALIAS_REJECT", reason: "AGGREGATE" };
|
|
11088
|
-
return { kind: "UNKNOWN", name };
|
|
11089
|
-
}
|
|
11090
|
-
function planPlainGroupByResolution(groupBy, columns, schemas) {
|
|
11091
11144
|
return {
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11145
|
+
capability: result.capability,
|
|
11146
|
+
reasons: [
|
|
11147
|
+
...result.reasons,
|
|
11148
|
+
{
|
|
11149
|
+
code: "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN",
|
|
11150
|
+
functionName: legacy.functionName,
|
|
11151
|
+
field: legacy.field,
|
|
11152
|
+
fieldType: legacy.fieldType,
|
|
11153
|
+
operator: legacy.operator
|
|
11154
|
+
}
|
|
11155
|
+
]
|
|
11095
11156
|
};
|
|
11096
11157
|
}
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
for (const [field, fv] of Object.entries(record)) {
|
|
11102
|
-
const val = fv.value;
|
|
11103
|
-
const strVal = val == null ? "" : typeof val === "string" ? val : JSON.stringify(val);
|
|
11104
|
-
if (alias) {
|
|
11105
|
-
row[`${alias}.${field}`] = strVal;
|
|
11106
|
-
row[field] = strVal;
|
|
11107
|
-
} else {
|
|
11108
|
-
row[field] = strVal;
|
|
11109
|
-
}
|
|
11110
|
-
}
|
|
11111
|
-
return row;
|
|
11158
|
+
function requireExactFunctionPushdown(result) {
|
|
11159
|
+
return requireExactLegacyKintoneFunctionPushdown(
|
|
11160
|
+
requireExactRelativeDatePushdown(result)
|
|
11161
|
+
);
|
|
11112
11162
|
}
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
}
|
|
11139
|
-
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
return
|
|
11163
|
-
}
|
|
11164
|
-
function assertJoinKeyAvailable(rows, key, savedColumns) {
|
|
11165
|
-
const missing = rows.length > 0 ? rows.some((row) => !Object.prototype.hasOwnProperty.call(row, key)) : savedColumns !== void 0 && !savedColumns.includes(key);
|
|
11166
|
-
if (missing) {
|
|
11167
|
-
throw new Error(`ArgumentError: JOIN key ${key} is not available in the materialized table.`);
|
|
11168
|
-
}
|
|
11169
|
-
}
|
|
11170
|
-
function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
11171
|
-
if (where === null) return rows;
|
|
11172
|
-
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
|
|
11163
|
+
|
|
11164
|
+
// src/core/optimization/joinPredicatePushdown.ts
|
|
11165
|
+
var SELECTION_TYPES = /* @__PURE__ */ new Set([
|
|
11166
|
+
"DROP_DOWN",
|
|
11167
|
+
"RADIO_BUTTON",
|
|
11168
|
+
"CHECK_BOX",
|
|
11169
|
+
"MULTI_SELECT",
|
|
11170
|
+
"STATUS"
|
|
11171
|
+
]);
|
|
11172
|
+
var KLIKE_TYPES = /* @__PURE__ */ new Set([
|
|
11173
|
+
"SINGLE_LINE_TEXT",
|
|
11174
|
+
"LINK",
|
|
11175
|
+
"MULTI_LINE_TEXT",
|
|
11176
|
+
"RICH_TEXT",
|
|
11177
|
+
"FILE"
|
|
11178
|
+
]);
|
|
11179
|
+
var DATETIME_EQUALITY_TYPES = /* @__PURE__ */ new Set([
|
|
11180
|
+
"DATETIME",
|
|
11181
|
+
"CREATED_TIME",
|
|
11182
|
+
"UPDATED_TIME"
|
|
11183
|
+
]);
|
|
11184
|
+
function resolveJoinFieldOwner(field, sources) {
|
|
11185
|
+
if (field.tableAlias !== null) {
|
|
11186
|
+
const aliases = sources.filter((source2) => source2.alias === field.tableAlias);
|
|
11187
|
+
if (aliases.length > 1) return Object.freeze({ status: "AMBIGUOUS" });
|
|
11188
|
+
if (aliases.length === 0) return Object.freeze({ status: "UNKNOWN" });
|
|
11189
|
+
const source = aliases[0];
|
|
11190
|
+
if (!source.fieldTypes.has(field.field)) return Object.freeze({ status: "UNKNOWN" });
|
|
11191
|
+
return owned(source, field.field);
|
|
11192
|
+
}
|
|
11193
|
+
const matches = sources.filter((source) => source.fieldTypes.has(field.field));
|
|
11194
|
+
if (matches.length > 1) return Object.freeze({ status: "AMBIGUOUS" });
|
|
11195
|
+
if (matches.length === 0) return Object.freeze({ status: "UNKNOWN" });
|
|
11196
|
+
return owned(matches[0], field.field);
|
|
11197
|
+
}
|
|
11198
|
+
function classifyJoinPushdownLeaf(predicate, sources) {
|
|
11199
|
+
if (predicate.left.type !== "FIELD") return unsafe();
|
|
11200
|
+
const owner = resolveJoinFieldOwner(predicate.left, sources);
|
|
11201
|
+
if (owner.status !== "OWNED" || owner.source.sourceKind !== "APP") return unsafe();
|
|
11202
|
+
const fieldType = owner.source.fieldTypes.get(owner.fieldCode);
|
|
11203
|
+
if (fieldType === void 0 || fieldType.startsWith("KSQL_")) return unsafe();
|
|
11204
|
+
const capability = classifyWhereCapability(predicate, (field) => {
|
|
11205
|
+
const resolved = resolveJoinFieldOwner(field, sources);
|
|
11206
|
+
if (resolved.status !== "OWNED" || resolved.source !== owner.source) return void 0;
|
|
11207
|
+
const type = resolved.source.fieldTypes.get(resolved.fieldCode);
|
|
11208
|
+
return type === void 0 ? void 0 : resolveFieldSemantics({ fieldType: type });
|
|
11209
|
+
});
|
|
11210
|
+
if (capability.capability !== "EXACT_PUSHDOWN") return unsafe();
|
|
11211
|
+
const relation = classifySupportedLeaf(predicate, owner, fieldType);
|
|
11212
|
+
return relation === "unsafe" ? unsafe() : Object.freeze({ relation, owner });
|
|
11173
11213
|
}
|
|
11174
|
-
function
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
);
|
|
11214
|
+
function buildJoinPushdownPlan(where, sources) {
|
|
11215
|
+
const allKlikes = /* @__PURE__ */ new Set();
|
|
11216
|
+
collectKlikes2(where, allKlikes);
|
|
11217
|
+
const fragments = where === null ? [] : mergeAndFragments(classifyTree(where, sources));
|
|
11218
|
+
const items = fragments.map((fragment) => Object.freeze({
|
|
11219
|
+
targetAlias: fragment.owner.alias,
|
|
11220
|
+
appId: fragment.owner.appId,
|
|
11221
|
+
predicate: fragment.predicate,
|
|
11222
|
+
relation: fragment.relation
|
|
11223
|
+
}));
|
|
11224
|
+
const appliedKlikes = /* @__PURE__ */ new Set();
|
|
11225
|
+
for (const item of items) collectKlikes2(item.predicate, appliedKlikes);
|
|
11226
|
+
return Object.freeze({
|
|
11227
|
+
items: Object.freeze(items),
|
|
11228
|
+
appliedKlikes,
|
|
11229
|
+
allKlikes: Object.freeze([...allKlikes]),
|
|
11230
|
+
rejections: Object.freeze(collectRejections(where, sources))
|
|
11231
|
+
});
|
|
11178
11232
|
}
|
|
11179
|
-
function
|
|
11180
|
-
if (
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
groupKey,
|
|
11188
|
-
row,
|
|
11189
|
-
resolutionPlan?.items[index],
|
|
11190
|
-
columns,
|
|
11191
|
-
aliasEvaluationContext
|
|
11192
|
-
)
|
|
11193
|
-
).join("\0");
|
|
11194
|
-
const bucket = groups.get(key);
|
|
11195
|
-
if (bucket) bucket.push(row);
|
|
11196
|
-
else groups.set(key, [row]);
|
|
11197
|
-
}
|
|
11198
|
-
if (groups.size === 0 && groupByKeys.length === 0 && hasAggregateColumns(columns)) {
|
|
11199
|
-
groups.set("", []);
|
|
11200
|
-
}
|
|
11201
|
-
const result = [];
|
|
11202
|
-
for (const groupRows of groups.values()) {
|
|
11203
|
-
const outRow = { ...groupRows[0] };
|
|
11204
|
-
for (const k of groupByKeys) {
|
|
11205
|
-
if (k.type === "ARITH_KEY") {
|
|
11206
|
-
outRow[arithColDefaultKey(k.expr)] = String(evalArithExpr(k.expr, groupRows[0]));
|
|
11207
|
-
} else if (k.type === "FUNC_KEY") {
|
|
11208
|
-
outRow[stringFuncDefaultKey(k.expr)] = evalStringFunc(k.expr, groupRows[0]);
|
|
11233
|
+
function collectRejections(where, sources) {
|
|
11234
|
+
if (where === null) return [];
|
|
11235
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
11236
|
+
const visit = (expr) => {
|
|
11237
|
+
if (expr.type === "BINARY") {
|
|
11238
|
+
if (expr.left.type !== "FIELD") {
|
|
11239
|
+
reasons.add("UNSAFE_RELATION");
|
|
11240
|
+
return;
|
|
11209
11241
|
}
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
return result;
|
|
11215
|
-
}
|
|
11216
|
-
function applyGroupingSets(rows, spec, columns, resolveAggSortKind, limits = {}) {
|
|
11217
|
-
const result = [];
|
|
11218
|
-
let generatedRows = 0;
|
|
11219
|
-
const countBucket = () => {
|
|
11220
|
-
generatedRows++;
|
|
11221
|
-
if (limits.maxGeneratedRows !== void 0 && generatedRows > limits.maxGeneratedRows) {
|
|
11222
|
-
throw new Error(
|
|
11223
|
-
`LimitError: B65 generated grouping rows ${generatedRows} exceed limit ${limits.maxGeneratedRows} (reason=GROUPING_OUTPUT_LIMIT_EXCEEDED).`
|
|
11224
|
-
);
|
|
11225
|
-
}
|
|
11226
|
-
};
|
|
11227
|
-
for (const set of spec.sets) {
|
|
11228
|
-
const root = { children: /* @__PURE__ */ new Map() };
|
|
11229
|
-
const buckets = [];
|
|
11230
|
-
for (const row of rows) {
|
|
11231
|
-
let node = root;
|
|
11232
|
-
for (const item of set.items) {
|
|
11233
|
-
const value = groupingItemValue(item, row);
|
|
11234
|
-
let child = node.children.get(value);
|
|
11235
|
-
if (!child) {
|
|
11236
|
-
child = { children: /* @__PURE__ */ new Map() };
|
|
11237
|
-
node.children.set(value, child);
|
|
11238
|
-
}
|
|
11239
|
-
node = child;
|
|
11242
|
+
const owner = resolveJoinFieldOwner(expr.left, sources);
|
|
11243
|
+
if (owner.status === "AMBIGUOUS") {
|
|
11244
|
+
reasons.add("AMBIGUOUS_FIELD");
|
|
11245
|
+
return;
|
|
11240
11246
|
}
|
|
11241
|
-
if (
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
buckets.push(node.rows);
|
|
11247
|
+
if (owner.status === "UNKNOWN") {
|
|
11248
|
+
reasons.add("UNKNOWN_FIELD_OR_METADATA");
|
|
11249
|
+
return;
|
|
11245
11250
|
}
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11251
|
+
if (containsFieldReference(expr.right)) {
|
|
11252
|
+
reasons.add("CROSS_TABLE_BINARY");
|
|
11253
|
+
return;
|
|
11254
|
+
}
|
|
11255
|
+
if (classifyJoinPushdownLeaf(expr, sources).relation === "unsafe") {
|
|
11256
|
+
reasons.add("UNSAFE_RELATION");
|
|
11257
|
+
}
|
|
11258
|
+
return;
|
|
11252
11259
|
}
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
if (!includedValues.has(item.canonicalId)) {
|
|
11259
|
-
includedValues.set(item.canonicalId, groupingItemValue(item, groupRows[0]));
|
|
11260
|
+
if (expr.type === "LOGICAL") {
|
|
11261
|
+
if (expr.op === "OR") {
|
|
11262
|
+
if (containsKlike2(expr)) {
|
|
11263
|
+
reasons.add("KLIKE_OR");
|
|
11264
|
+
return;
|
|
11260
11265
|
}
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
outRow[item.unqualifiedBridgeKey] = value;
|
|
11266
|
+
const left = classifyTree(expr.left, sources);
|
|
11267
|
+
const right = classifyTree(expr.right, sources);
|
|
11268
|
+
if (left.length !== 1 || right.length !== 1 || !sameOwner(left[0].owner, right[0].owner)) {
|
|
11269
|
+
reasons.add("CROSS_ALIAS_OR");
|
|
11270
|
+
return;
|
|
11267
11271
|
}
|
|
11268
11272
|
}
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11273
|
+
visit(expr.left);
|
|
11274
|
+
visit(expr.right);
|
|
11275
|
+
return;
|
|
11272
11276
|
}
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
function groupingItemValue(item, row) {
|
|
11277
|
-
if (!row) return "";
|
|
11278
|
-
return row[item.directKey] ?? (item.unqualifiedBridgeKey === null ? void 0 : row[item.unqualifiedBridgeKey]) ?? "";
|
|
11279
|
-
}
|
|
11280
|
-
function materializeAggregateColumns(outRow, groupRows, columns, resolveAggSortKind) {
|
|
11281
|
-
for (const col of columns) {
|
|
11282
|
-
if (col.type === "AGGREGATE") {
|
|
11283
|
-
const syntheticKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
|
|
11284
|
-
const value = String(evalAggregate(col.func, col.distinct, col.arg, col.separator, groupRows, resolveAggSortKind));
|
|
11285
|
-
outRow[col.alias ?? syntheticKey] = value;
|
|
11286
|
-
if (col.alias) outRow[syntheticKey] = value;
|
|
11287
|
-
} else if (col.type === "ARITH_AGG_COL") {
|
|
11288
|
-
const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
|
|
11289
|
-
outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows, resolveAggSortKind));
|
|
11290
|
-
} else if (col.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(col.expr)) {
|
|
11291
|
-
const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
|
|
11292
|
-
const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
|
|
11293
|
-
outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
|
|
11294
|
-
} else if (col.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(col.expr)) {
|
|
11295
|
-
const outputKey = col.alias ?? scalarValueDefaultKey(col.expr);
|
|
11296
|
-
const resolvedExpr = resolveAggInScalarValue(col.expr, groupRows, resolveAggSortKind);
|
|
11297
|
-
outRow[outputKey] = String(evalScalarValueExpr(resolvedExpr, outRow));
|
|
11277
|
+
if (expr.type === "GROUP") {
|
|
11278
|
+
visit(expr.expr);
|
|
11279
|
+
return;
|
|
11298
11280
|
}
|
|
11299
|
-
|
|
11281
|
+
if (expr.type === "NOT") {
|
|
11282
|
+
reasons.add("NOT");
|
|
11283
|
+
return;
|
|
11284
|
+
}
|
|
11285
|
+
reasons.add("UNSAFE_RELATION");
|
|
11286
|
+
};
|
|
11287
|
+
visit(where);
|
|
11288
|
+
return [...reasons].map((reason) => Object.freeze({ reason }));
|
|
11300
11289
|
}
|
|
11301
|
-
function
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11290
|
+
function serializeJoinPushdownItem(item, sources) {
|
|
11291
|
+
assertPredicateOwnership(item.predicate, item, sources);
|
|
11292
|
+
return whereToKintone(item.predicate);
|
|
11293
|
+
}
|
|
11294
|
+
function assertPredicateOwnership(predicate, item, sources) {
|
|
11295
|
+
switch (predicate.type) {
|
|
11296
|
+
case "LOGICAL":
|
|
11297
|
+
assertPredicateOwnership(predicate.left, item, sources);
|
|
11298
|
+
assertPredicateOwnership(predicate.right, item, sources);
|
|
11299
|
+
return;
|
|
11300
|
+
case "GROUP":
|
|
11301
|
+
assertPredicateOwnership(predicate.expr, item, sources);
|
|
11302
|
+
return;
|
|
11303
|
+
case "BINARY": {
|
|
11304
|
+
if (predicate.left.type !== "FIELD" || containsFieldReference(predicate.right)) {
|
|
11305
|
+
throw joinOwnershipError(item, "predicate contains a non-target or RHS field reference");
|
|
11311
11306
|
}
|
|
11312
|
-
const
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
resolution.columnIndex,
|
|
11316
|
-
aliasEvaluationContext
|
|
11317
|
-
);
|
|
11318
|
-
if (typeof value !== "string") {
|
|
11319
|
-
throw new Error("InternalError: GROUP BY alias resolved to an expanded SELECT column.");
|
|
11307
|
+
const owner = resolveJoinFieldOwner(predicate.left, sources);
|
|
11308
|
+
if (owner.status !== "OWNED" || owner.alias !== item.targetAlias || owner.appId !== item.appId || owner.source.sourceKind !== "APP") {
|
|
11309
|
+
throw joinOwnershipError(item, `field ${predicate.left.field} is not uniquely owned by the target`);
|
|
11320
11310
|
}
|
|
11321
|
-
return
|
|
11311
|
+
return;
|
|
11322
11312
|
}
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11313
|
+
case "NOT":
|
|
11314
|
+
case "NULL_CHECK":
|
|
11315
|
+
case "EXISTS":
|
|
11316
|
+
case "BOOLEAN":
|
|
11317
|
+
throw joinOwnershipError(item, "predicate shape is outside the Phase A serializer contract");
|
|
11326
11318
|
}
|
|
11327
|
-
if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
|
|
11328
|
-
return String(evalArithExpr(key.expr, row));
|
|
11329
11319
|
}
|
|
11330
|
-
function
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
const
|
|
11339
|
-
if (
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
} else {
|
|
11346
|
-
const value = evalScalarValueExprNullable(arg, row);
|
|
11347
|
-
if (value === null) continue;
|
|
11348
|
-
if (value === "" && func !== "MIN" && func !== "MAX") continue;
|
|
11349
|
-
if (typeof value === "number" && Number.isNaN(value)) continue;
|
|
11350
|
-
strVal = String(value);
|
|
11351
|
-
}
|
|
11352
|
-
strValues.push(strVal);
|
|
11353
|
-
}
|
|
11354
|
-
const statistical = func === "STDDEV_POP" || func === "STDDEV_SAMP" || func === "VAR_POP" || func === "VAR_SAMP" || func === "MEDIAN";
|
|
11355
|
-
const numericValues = statistical ? strValues.map((value) => {
|
|
11356
|
-
const numeric = Number(value);
|
|
11357
|
-
if (!Number.isFinite(numeric)) {
|
|
11358
|
-
throw new Error(`ArgumentError: ${func} \u306E\u5F15\u6570\u306B\u975E\u6570\u5024\u307E\u305F\u306F\u975E\u6709\u9650\u306E\u5024\u304C\u3042\u308A\u307E\u3059: ${value}`);
|
|
11320
|
+
function joinOwnershipError(item, reason) {
|
|
11321
|
+
return new Error(
|
|
11322
|
+
`InternalError: JOIN pushdown ownership guard failed for ${item.targetAlias}/APP${item.appId}: ${reason}.`
|
|
11323
|
+
);
|
|
11324
|
+
}
|
|
11325
|
+
function classifyTree(where, sources) {
|
|
11326
|
+
switch (where.type) {
|
|
11327
|
+
case "BINARY": {
|
|
11328
|
+
const classification = classifyJoinPushdownLeaf(where, sources);
|
|
11329
|
+
if (classification.relation === "unsafe" || classification.owner === void 0) return [];
|
|
11330
|
+
return [{
|
|
11331
|
+
owner: classification.owner,
|
|
11332
|
+
predicate: where,
|
|
11333
|
+
relation: classification.relation
|
|
11334
|
+
}];
|
|
11359
11335
|
}
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
if (frequency > maxFrequency) {
|
|
11375
|
-
result = candidate;
|
|
11376
|
-
maxFrequency = frequency;
|
|
11377
|
-
continue;
|
|
11378
|
-
}
|
|
11379
|
-
if (frequency !== maxFrequency) continue;
|
|
11380
|
-
const canonical = compareCanonicalValues(candidate, result, semantics);
|
|
11381
|
-
if (canonical < 0 || canonical === 0 && compareCodePointStrings(candidate, result) < 0) {
|
|
11382
|
-
result = candidate;
|
|
11383
|
-
}
|
|
11336
|
+
case "LOGICAL": {
|
|
11337
|
+
const left = classifyTree(where.left, sources);
|
|
11338
|
+
const right = classifyTree(where.right, sources);
|
|
11339
|
+
if (where.op === "AND") return mergeAndFragments([...left, ...right]);
|
|
11340
|
+
if (containsKlike2(where)) return [];
|
|
11341
|
+
if (left.length !== 1 || right.length !== 1) return [];
|
|
11342
|
+
const a = left[0];
|
|
11343
|
+
const b = right[0];
|
|
11344
|
+
if (!sameOwner(a.owner, b.owner)) return [];
|
|
11345
|
+
return [{
|
|
11346
|
+
owner: a.owner,
|
|
11347
|
+
predicate: { type: "LOGICAL", op: "OR", left: a.predicate, right: b.predicate },
|
|
11348
|
+
relation: combineRelation(a.relation, b.relation)
|
|
11349
|
+
}];
|
|
11384
11350
|
}
|
|
11385
|
-
|
|
11351
|
+
case "GROUP":
|
|
11352
|
+
return classifyTree(where.expr, sources).map((fragment) => ({
|
|
11353
|
+
...fragment,
|
|
11354
|
+
predicate: { type: "GROUP", expr: fragment.predicate }
|
|
11355
|
+
}));
|
|
11356
|
+
case "NOT":
|
|
11357
|
+
case "NULL_CHECK":
|
|
11358
|
+
case "EXISTS":
|
|
11359
|
+
case "BOOLEAN":
|
|
11360
|
+
return [];
|
|
11386
11361
|
}
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11362
|
+
}
|
|
11363
|
+
function mergeAndFragments(fragments) {
|
|
11364
|
+
const merged = [];
|
|
11365
|
+
for (const fragment of fragments) {
|
|
11366
|
+
const index = merged.findIndex((candidate) => sameOwner(candidate.owner, fragment.owner));
|
|
11367
|
+
if (index < 0) {
|
|
11368
|
+
merged.push(fragment);
|
|
11369
|
+
continue;
|
|
11394
11370
|
}
|
|
11395
|
-
|
|
11371
|
+
const current = merged[index];
|
|
11372
|
+
merged[index] = {
|
|
11373
|
+
owner: current.owner,
|
|
11374
|
+
predicate: {
|
|
11375
|
+
type: "LOGICAL",
|
|
11376
|
+
op: "AND",
|
|
11377
|
+
left: current.predicate,
|
|
11378
|
+
right: fragment.predicate
|
|
11379
|
+
},
|
|
11380
|
+
relation: combineRelation(current.relation, fragment.relation)
|
|
11381
|
+
};
|
|
11396
11382
|
}
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
-
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
|
|
11406
|
-
const middle = Math.floor(sorted.length / 2);
|
|
11407
|
-
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
11408
|
-
}
|
|
11409
|
-
case "VAR_POP":
|
|
11410
|
-
case "VAR_SAMP":
|
|
11411
|
-
case "STDDEV_POP":
|
|
11412
|
-
case "STDDEV_SAMP": {
|
|
11413
|
-
const sample = func === "VAR_SAMP" || func === "STDDEV_SAMP";
|
|
11414
|
-
if (nums.length === 0 || sample && nums.length < 2) return "";
|
|
11415
|
-
const origin = nums[0];
|
|
11416
|
-
let count = 0;
|
|
11417
|
-
let mean = 0;
|
|
11418
|
-
let m2 = 0;
|
|
11419
|
-
for (const rawValue of nums) {
|
|
11420
|
-
const value = rawValue - origin;
|
|
11421
|
-
count++;
|
|
11422
|
-
const delta = value - mean;
|
|
11423
|
-
mean += delta / count;
|
|
11424
|
-
m2 += delta * (value - mean);
|
|
11425
|
-
}
|
|
11426
|
-
const variance = Math.max(0, m2 / (sample ? count - 1 : count));
|
|
11427
|
-
return func === "STDDEV_POP" || func === "STDDEV_SAMP" ? Math.sqrt(variance) : variance;
|
|
11428
|
-
}
|
|
11383
|
+
return merged;
|
|
11384
|
+
}
|
|
11385
|
+
function classifySupportedLeaf(predicate, owner, fieldType) {
|
|
11386
|
+
if (predicate.op === "LIKE" || predicate.op === "NOT_LIKE") return "unsafe";
|
|
11387
|
+
if (predicate.op === "KLIKE" || predicate.op === "NOT_KLIKE") {
|
|
11388
|
+
return KLIKE_TYPES.has(fieldType) && predicate.right.type === "STRING" && predicate.right.value !== "" ? "exact" : "unsafe";
|
|
11389
|
+
}
|
|
11390
|
+
if (fieldType === "__ID__" || owner.fieldCode === "$id") {
|
|
11391
|
+
return isPositiveSafeInteger(predicate.right) && (predicate.op === "=" || predicate.op === "<" || predicate.op === ">" || predicate.op === "<=" || predicate.op === ">=") ? "exact" : "unsafe";
|
|
11429
11392
|
}
|
|
11393
|
+
if (fieldType === "RECORD_NUMBER") {
|
|
11394
|
+
return "unsafe";
|
|
11395
|
+
}
|
|
11396
|
+
if (fieldType === "NUMBER") {
|
|
11397
|
+
if (predicate.right.type !== "NUMBER") return "unsafe";
|
|
11398
|
+
if (predicate.op === "=") return "superset";
|
|
11399
|
+
return (predicate.op === "<" || predicate.op === ">") && isSafeIntegerLiteral(predicate.right) ? "superset" : "unsafe";
|
|
11400
|
+
}
|
|
11401
|
+
if (fieldType === "SINGLE_LINE_TEXT") {
|
|
11402
|
+
return predicate.op === "=" && predicate.right.type === "STRING" && predicate.right.value !== "" ? "superset" : "unsafe";
|
|
11403
|
+
}
|
|
11404
|
+
if (fieldType === "DATE" || fieldType === "TIME" || DATETIME_EQUALITY_TYPES.has(fieldType)) {
|
|
11405
|
+
if (predicate.op !== "=" || predicate.right.type !== "STRING") return "unsafe";
|
|
11406
|
+
if (fieldType === "DATE") return isCanonicalDate(predicate.right.value) ? "superset" : "unsafe";
|
|
11407
|
+
if (fieldType === "TIME") return isCanonicalTime(predicate.right.value) ? "superset" : "unsafe";
|
|
11408
|
+
return isCanonicalDateTime(predicate.right.value) ? "superset" : "unsafe";
|
|
11409
|
+
}
|
|
11410
|
+
if (SELECTION_TYPES.has(fieldType)) {
|
|
11411
|
+
if (predicate.op !== "IN" && predicate.op !== "NOT_IN" || predicate.right.type !== "IN_LIST" || predicate.right.values.length === 0) return "unsafe";
|
|
11412
|
+
const options = owner.source.fieldOptions?.get(owner.fieldCode);
|
|
11413
|
+
if (options === void 0) return "unsafe";
|
|
11414
|
+
return predicate.right.values.every(
|
|
11415
|
+
(value) => value.type === "STRING" && value.value !== "" && options.has(value.value)
|
|
11416
|
+
) ? "exact" : "unsafe";
|
|
11417
|
+
}
|
|
11418
|
+
return "unsafe";
|
|
11419
|
+
}
|
|
11420
|
+
function owned(source, fieldCode) {
|
|
11421
|
+
return Object.freeze({
|
|
11422
|
+
status: "OWNED",
|
|
11423
|
+
alias: source.alias,
|
|
11424
|
+
appId: source.appId,
|
|
11425
|
+
fieldCode,
|
|
11426
|
+
source
|
|
11427
|
+
});
|
|
11430
11428
|
}
|
|
11431
|
-
function
|
|
11432
|
-
|
|
11433
|
-
return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
|
|
11429
|
+
function unsafe() {
|
|
11430
|
+
return Object.freeze({ relation: "unsafe" });
|
|
11434
11431
|
}
|
|
11435
|
-
function
|
|
11436
|
-
|
|
11437
|
-
if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
|
|
11438
|
-
const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
|
|
11439
|
-
const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
|
|
11440
|
-
switch (node.op) {
|
|
11441
|
-
case "+":
|
|
11442
|
-
return l + r;
|
|
11443
|
-
case "-":
|
|
11444
|
-
return l - r;
|
|
11445
|
-
case "*":
|
|
11446
|
-
return l * r;
|
|
11447
|
-
case "/":
|
|
11448
|
-
return r !== 0 ? l / r : NaN;
|
|
11449
|
-
case "%":
|
|
11450
|
-
return r !== 0 ? l % r : NaN;
|
|
11451
|
-
}
|
|
11432
|
+
function sameOwner(left, right) {
|
|
11433
|
+
return left.alias === right.alias && left.appId === right.appId && left.source === right.source;
|
|
11452
11434
|
}
|
|
11453
|
-
function
|
|
11454
|
-
return
|
|
11435
|
+
function combineRelation(left, right) {
|
|
11436
|
+
return left === "exact" && right === "exact" ? "exact" : "superset";
|
|
11455
11437
|
}
|
|
11456
|
-
function
|
|
11457
|
-
|
|
11458
|
-
if (arg.type === "FIELD") return resolver?.(arg) ?? "string";
|
|
11459
|
-
if (arg.type === "NUMBER" || arg.type === "ARITH" || arg.type === "SCALAR_ARITH") return "number";
|
|
11460
|
-
if (arg.type === "STRING" || arg.type === "CONCAT_OP" || arg.type === "VARIABLE") return "string";
|
|
11461
|
-
if (arg.type === "STRING_FUNC") {
|
|
11462
|
-
const numeric = /* @__PURE__ */ new Set(["LENGTH", "LENGTH_CHAR", "INSTR", "ROUND", "FLOOR", "CEIL", "TRUNCATE", "YEAR", "MONTH", "DAY", "DATEDIFF", "ABS", "MOD", "POWER", "SQRT", "DAYOFWEEK", "QUARTER", "WEEK"]);
|
|
11463
|
-
if (arg.func === "CAST") return arg.args[1]?.type === "STRING" && arg.args[1].value === "NUMBER" ? "number" : "string";
|
|
11464
|
-
return numeric.has(arg.func) ? "number" : "string";
|
|
11465
|
-
}
|
|
11466
|
-
const results = [...arg.branches.map((branch) => branch.result), ...arg.elseResult === null ? [] : [arg.elseResult]].filter((result) => result.type !== "ARRAY").map((result) => resolveAggregateArgSemantics(result, resolver));
|
|
11467
|
-
if (results.length === 0 || results.some((result) => result === void 0)) return "string";
|
|
11468
|
-
const kinds = results.map((result) => typeof result === "string" ? result : result.compareMode === "number" || result.compareMode === "recordNumber" ? "number" : "string");
|
|
11469
|
-
if (!kinds.every((kind) => kind === kinds[0])) return "string";
|
|
11470
|
-
const first = results[0];
|
|
11471
|
-
return results.every((result) => JSON.stringify(result) === JSON.stringify(first)) ? first : kinds[0];
|
|
11438
|
+
function isPositiveSafeInteger(value) {
|
|
11439
|
+
return value.type === "NUMBER" && isSafeIntegerLiteral(value) && value.value > 0;
|
|
11472
11440
|
}
|
|
11473
|
-
function
|
|
11474
|
-
|
|
11475
|
-
return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
|
|
11441
|
+
function isSafeIntegerLiteral(value) {
|
|
11442
|
+
return /^-?\d+$/.test(numberLiteralText(value)) && Number.isSafeInteger(value.value);
|
|
11476
11443
|
}
|
|
11477
|
-
function
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
return rows.filter((row) => {
|
|
11486
|
-
const key = keyFor(row);
|
|
11487
|
-
if (seen.has(key)) return false;
|
|
11488
|
-
seen.add(key);
|
|
11489
|
-
return true;
|
|
11490
|
-
});
|
|
11444
|
+
function isCanonicalDate(value) {
|
|
11445
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
11446
|
+
if (!match) return false;
|
|
11447
|
+
const year = Number(match[1]);
|
|
11448
|
+
const month = Number(match[2]);
|
|
11449
|
+
const day = Number(match[3]);
|
|
11450
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
11451
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
11491
11452
|
}
|
|
11492
|
-
function
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11453
|
+
function isCanonicalTime(value) {
|
|
11454
|
+
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
11455
|
+
return match !== null && Number(match[1]) <= 23 && Number(match[2]) <= 59;
|
|
11456
|
+
}
|
|
11457
|
+
function isCanonicalDateTime(value) {
|
|
11458
|
+
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.exec(value);
|
|
11459
|
+
if (!match || !isCanonicalDate(match[1])) return false;
|
|
11460
|
+
return Number(match[2]) <= 23 && Number(match[3]) <= 59 && Number(match[4]) <= 59;
|
|
11461
|
+
}
|
|
11462
|
+
function collectKlikes2(where, out) {
|
|
11463
|
+
if (where === null) return;
|
|
11464
|
+
if (isKlike(where)) {
|
|
11465
|
+
out.add(where);
|
|
11466
|
+
return;
|
|
11500
11467
|
}
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11468
|
+
switch (where.type) {
|
|
11469
|
+
case "LOGICAL":
|
|
11470
|
+
collectKlikes2(where.left, out);
|
|
11471
|
+
collectKlikes2(where.right, out);
|
|
11472
|
+
return;
|
|
11473
|
+
case "NOT":
|
|
11474
|
+
case "GROUP":
|
|
11475
|
+
collectKlikes2(where.expr, out);
|
|
11476
|
+
return;
|
|
11477
|
+
case "BINARY":
|
|
11478
|
+
case "NULL_CHECK":
|
|
11479
|
+
case "EXISTS":
|
|
11480
|
+
case "BOOLEAN":
|
|
11481
|
+
return;
|
|
11510
11482
|
}
|
|
11511
|
-
const distinctContext = {
|
|
11512
|
-
...context,
|
|
11513
|
-
wildcardKeys: sortedWildcardKeys,
|
|
11514
|
-
parentWildcardKeys: sortedParentKeys
|
|
11515
|
-
};
|
|
11516
|
-
return (row) => JSON.stringify(buildDistinctTuple(columns, row, distinctContext));
|
|
11517
11483
|
}
|
|
11518
|
-
function
|
|
11519
|
-
|
|
11520
|
-
|
|
11484
|
+
function containsKlike2(where) {
|
|
11485
|
+
const found = /* @__PURE__ */ new Set();
|
|
11486
|
+
collectKlikes2(where, found);
|
|
11487
|
+
return found.size > 0;
|
|
11521
11488
|
}
|
|
11522
|
-
function
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
if (key.type === "GROUPING_KEY") return { semantics: syntheticSemantics("number") };
|
|
11529
|
-
const semantics = fieldSemantics2?.get(key.name);
|
|
11530
|
-
if (semantics) return { semantics };
|
|
11531
|
-
const orderMap = optionOrders?.get(key.name);
|
|
11532
|
-
if (orderMap) {
|
|
11533
|
-
return {
|
|
11534
|
-
semantics: {
|
|
11535
|
-
fieldType: "MULTI_SELECT",
|
|
11536
|
-
compareMode: "option",
|
|
11537
|
-
inSubtable: false,
|
|
11538
|
-
requiresCollectionOperators: false,
|
|
11539
|
-
optionOrder: orderMap
|
|
11540
|
-
}
|
|
11541
|
-
};
|
|
11542
|
-
}
|
|
11543
|
-
return { semantics: syntheticSemantics(sortKinds?.get(key.name) ?? "string") };
|
|
11544
|
-
});
|
|
11545
|
-
const decorated = rows.map((row) => ({
|
|
11546
|
-
row,
|
|
11547
|
-
keys: orderBy.map(({ key }, i) => {
|
|
11548
|
-
const s = evalOrderKey(key, row, aliasEvaluator);
|
|
11549
|
-
return { s };
|
|
11550
|
-
})
|
|
11551
|
-
}));
|
|
11552
|
-
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
11553
|
-
decorated.sort(compare);
|
|
11554
|
-
return { rows: decorated, compare };
|
|
11489
|
+
function containsFieldReference(value) {
|
|
11490
|
+
if (value === null || typeof value !== "object") return false;
|
|
11491
|
+
if (Array.isArray(value)) return value.some(containsFieldReference);
|
|
11492
|
+
const record = value;
|
|
11493
|
+
if (record.type === "FIELD" || record.type === "FIELD_REF") return true;
|
|
11494
|
+
return Object.values(record).some(containsFieldReference);
|
|
11555
11495
|
}
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11496
|
+
|
|
11497
|
+
// src/core/optimization/applyParentSelectionPlan.ts
|
|
11498
|
+
function buildApplyParentSelectionPlan(where, metadata) {
|
|
11499
|
+
const plan = buildSingleTableKlikePushdownPlan(where, {
|
|
11500
|
+
allowUnqualifiedFields: true,
|
|
11501
|
+
allowKlike: true,
|
|
11502
|
+
fieldTypes: metadata.fieldTypes,
|
|
11503
|
+
fieldOptions: metadata.fieldOptions
|
|
11504
|
+
});
|
|
11505
|
+
return {
|
|
11506
|
+
prefilter: plan.condition,
|
|
11507
|
+
appliedKlikes: plan.appliedKlikes,
|
|
11508
|
+
unappliedKlikes: plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr))
|
|
11509
|
+
};
|
|
11562
11510
|
}
|
|
11563
|
-
|
|
11564
|
-
|
|
11511
|
+
|
|
11512
|
+
// src/core/optimization/canonicalOrderPlanner.ts
|
|
11513
|
+
var REST_OFFSET_MAX = 1e4;
|
|
11514
|
+
var REST_LIMIT_MAX = 500;
|
|
11515
|
+
function fieldSemantics(item, semantics) {
|
|
11516
|
+
return item.key.type === "FIELD_NAME" ? semantics.get(item.key.name) : void 0;
|
|
11565
11517
|
}
|
|
11566
|
-
|
|
11567
|
-
"
|
|
11568
|
-
"LENGTH_CHAR",
|
|
11569
|
-
"INSTR",
|
|
11570
|
-
"ROUND",
|
|
11571
|
-
"FLOOR",
|
|
11572
|
-
"CEIL",
|
|
11573
|
-
"TRUNCATE",
|
|
11574
|
-
"YEAR",
|
|
11575
|
-
"MONTH",
|
|
11576
|
-
"DAY",
|
|
11577
|
-
"DATEDIFF",
|
|
11578
|
-
"ABS",
|
|
11579
|
-
"MOD",
|
|
11580
|
-
"POWER",
|
|
11581
|
-
"SQRT",
|
|
11582
|
-
"DAYOFWEEK",
|
|
11583
|
-
"QUARTER",
|
|
11584
|
-
"WEEK"
|
|
11585
|
-
]);
|
|
11586
|
-
function evalOrderKey(key, row, aliasEvaluator) {
|
|
11587
|
-
switch (key.type) {
|
|
11588
|
-
case "FIELD_NAME":
|
|
11589
|
-
return aliasEvaluator?.(key.name, row) ?? row[key.name] ?? "";
|
|
11590
|
-
case "ARITH_KEY":
|
|
11591
|
-
return String(evalArithExpr(key.expr, row));
|
|
11592
|
-
case "FUNC_KEY":
|
|
11593
|
-
return evalStringFunc(key.expr, row);
|
|
11594
|
-
case "GROUPING_KEY":
|
|
11595
|
-
return evalGroupingRef(key.ref, row);
|
|
11596
|
-
}
|
|
11518
|
+
function hasSelectOutputAlias(stmt, name) {
|
|
11519
|
+
return stmt.columns.some((column) => "alias" in column && column.alias === name);
|
|
11597
11520
|
}
|
|
11598
|
-
function
|
|
11599
|
-
const
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
const source = aggregateSyntheticName(column.func, column.distinct, column.arg);
|
|
11612
|
-
evaluators.set(alias, (row) => row[alias] ?? row[source] ?? "0");
|
|
11613
|
-
break;
|
|
11614
|
-
}
|
|
11615
|
-
case "ARITH_AGG_COL": {
|
|
11616
|
-
const source = aggArithDefaultKey(column.expr);
|
|
11617
|
-
evaluators.set(alias, (row) => row[alias] ?? row[source] ?? "0");
|
|
11618
|
-
break;
|
|
11619
|
-
}
|
|
11620
|
-
case "WINDOW_COL":
|
|
11621
|
-
evaluators.set(alias, (row) => row[alias] ?? "");
|
|
11622
|
-
break;
|
|
11623
|
-
case "ARITH_COL":
|
|
11624
|
-
evaluators.set(alias, (row) => String(evalArithExpr(column.expr, row)));
|
|
11625
|
-
break;
|
|
11626
|
-
case "STRFUNC_COL": {
|
|
11627
|
-
const source = stringFuncDefaultKey(column.expr);
|
|
11628
|
-
evaluators.set(alias, (row) => hasAggregateInStringFuncExpr2(column.expr) ? row[alias] ?? row[source] ?? evalStringFunc(column.expr, row, resolveFieldType, resolveFieldSemantics2) : evalStringFunc(column.expr, row, resolveFieldType, resolveFieldSemantics2));
|
|
11629
|
-
break;
|
|
11630
|
-
}
|
|
11631
|
-
case "CASE_COL":
|
|
11632
|
-
evaluators.set(alias, (row) => evalCaseWhen(column.expr, row, resolveFieldType, resolveFieldSemantics2));
|
|
11633
|
-
break;
|
|
11634
|
-
case "SCALAR_VALUE_COL": {
|
|
11635
|
-
const source = scalarValueDefaultKey(column.expr);
|
|
11636
|
-
evaluators.set(alias, (row) => scalarValueHasAggregate2(column.expr) ? row[alias] ?? row[source] ?? "" : String(evalScalarValueExpr(column.expr, row, resolveFieldType, resolveFieldSemantics2)));
|
|
11637
|
-
break;
|
|
11638
|
-
}
|
|
11639
|
-
case "SCALAR_SUBQUERY_COL":
|
|
11640
|
-
evaluators.set(alias, () => scalarCache?.get(columnIndex) ?? "");
|
|
11641
|
-
break;
|
|
11642
|
-
case "GROUPING_COL":
|
|
11643
|
-
evaluators.set(alias, (row) => evalGroupingRef(column.ref, row));
|
|
11644
|
-
break;
|
|
11645
|
-
case "VARIABLE_COL":
|
|
11646
|
-
break;
|
|
11521
|
+
function planCanonicalOrder(input) {
|
|
11522
|
+
const { stmt } = input;
|
|
11523
|
+
const reasons = [];
|
|
11524
|
+
const windowOrderBy = stmt.columns.flatMap(
|
|
11525
|
+
(column) => column.type === "WINDOW_COL" ? column.orderBy : []
|
|
11526
|
+
);
|
|
11527
|
+
const allOrderBy = [...stmt.orderBy, ...windowOrderBy];
|
|
11528
|
+
for (const item of allOrderBy) {
|
|
11529
|
+
if (item.key.type !== "FIELD_NAME") continue;
|
|
11530
|
+
const semantics = fieldSemantics(item, input.orderSemantics);
|
|
11531
|
+
if (!semantics) {
|
|
11532
|
+
reasons.push("ORDER_KEY_UNRESOLVED");
|
|
11533
|
+
continue;
|
|
11647
11534
|
}
|
|
11535
|
+
if (semantics.fieldType === "KSQL_AMBIGUOUS") reasons.push("ORDER_KEY_AMBIGUOUS");
|
|
11536
|
+
else if (semantics.compareMode === "unsupported") reasons.push("ORDER_KEY_UNSUPPORTED");
|
|
11648
11537
|
}
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
if (rows.length === 0 || windows.length === 0) return rows;
|
|
11654
|
-
for (const window of windows) {
|
|
11655
|
-
const partitions = /* @__PURE__ */ new Map();
|
|
11656
|
-
for (const row of rows) {
|
|
11657
|
-
const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
|
|
11658
|
-
const partition = partitions.get(key);
|
|
11659
|
-
if (partition) partition.push(row);
|
|
11660
|
-
else partitions.set(key, [row]);
|
|
11661
|
-
}
|
|
11662
|
-
for (const partition of partitions.values()) {
|
|
11663
|
-
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
|
|
11664
|
-
const sorted = sortedResult.rows;
|
|
11665
|
-
let rank = 1;
|
|
11666
|
-
let denseRank = 1;
|
|
11667
|
-
for (let index = 0; index < sorted.length; index++) {
|
|
11668
|
-
if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
|
|
11669
|
-
rank = index + 1;
|
|
11670
|
-
denseRank++;
|
|
11671
|
-
}
|
|
11672
|
-
const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
|
|
11673
|
-
sorted[index].row[window.alias] = String(value);
|
|
11674
|
-
}
|
|
11675
|
-
}
|
|
11538
|
+
if (reasons.includes("ORDER_KEY_AMBIGUOUS")) {
|
|
11539
|
+
throw new Error(
|
|
11540
|
+
"ArgumentError: ORDER BY key is an ambiguous column reference (reason=ORDER_KEY_AMBIGUOUS). Qualify the key with its table alias."
|
|
11541
|
+
);
|
|
11676
11542
|
}
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11543
|
+
if (reasons.includes("ORDER_KEY_UNSUPPORTED") || reasons.includes("ORDER_KEY_UNRESOLVED")) {
|
|
11544
|
+
const reason = reasons.includes("ORDER_KEY_UNSUPPORTED") ? "ORDER_KEY_UNSUPPORTED" : "ORDER_KEY_UNRESOLVED";
|
|
11545
|
+
throw new Error(`ArgumentError: ORDER BY key has no canonical comparison contract (reason=${reason}).`);
|
|
11546
|
+
}
|
|
11547
|
+
const allRestEquivalent = stmt.orderBy.length > 0 && windowOrderBy.length === 0 && stmt.orderBy.every(
|
|
11548
|
+
(item) => item.key.type === "FIELD_NAME" && item.key.name === "$id" && !hasSelectOutputAlias(stmt, item.key.name)
|
|
11549
|
+
);
|
|
11550
|
+
if (!allRestEquivalent) reasons.push("ORDER_KEY_NOT_REST_EQUIVALENT");
|
|
11551
|
+
if (input.whereCapability !== "EXACT_PUSHDOWN") reasons.push("WHERE_NOT_EXACT");
|
|
11552
|
+
if (input.staticMode !== "SIMPLE") reasons.push("QUERY_SHAPE_LOCAL");
|
|
11553
|
+
if (stmt.limit === null || stmt.limit < 0 || stmt.limit > REST_LIMIT_MAX) {
|
|
11554
|
+
reasons.push("LIMIT_NOT_REST_WINDOW");
|
|
11555
|
+
}
|
|
11556
|
+
if ((stmt.offset ?? 0) < 0 || (stmt.offset ?? 0) > REST_OFFSET_MAX) {
|
|
11557
|
+
reasons.push("OFFSET_NOT_REST_WINDOW");
|
|
11558
|
+
}
|
|
11559
|
+
if (stmt.limit !== null && stmt.limit > input.maxRecords) reasons.push("MAX_RECORDS_WINDOW");
|
|
11560
|
+
if (input.hasKlike) reasons.push("KLIKE_NOT_REST_WINDOW");
|
|
11561
|
+
if (reasons.length === 0) {
|
|
11562
|
+
return {
|
|
11563
|
+
kind: "CANONICAL_REST_TOP_N",
|
|
11564
|
+
requiresCompleteInput: false,
|
|
11565
|
+
localOrderBy: false,
|
|
11566
|
+
applyLocalOffsetLimit: false,
|
|
11567
|
+
reasonCodes: []
|
|
11568
|
+
};
|
|
11569
|
+
}
|
|
11570
|
+
return {
|
|
11571
|
+
kind: "CANONICAL_LOCAL",
|
|
11572
|
+
requiresCompleteInput: allOrderBy.length > 0,
|
|
11573
|
+
localOrderBy: stmt.orderBy.length > 0,
|
|
11574
|
+
applyLocalOffsetLimit: stmt.orderBy.length > 0,
|
|
11575
|
+
reasonCodes: [...new Set(reasons)]
|
|
11576
|
+
};
|
|
11682
11577
|
}
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11578
|
+
|
|
11579
|
+
// src/core/optimization/korderPlanner.ts
|
|
11580
|
+
var KORDER_NATIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
11581
|
+
"RECORD_NUMBER",
|
|
11582
|
+
"SINGLE_LINE_TEXT",
|
|
11583
|
+
"NUMBER",
|
|
11584
|
+
"CALC",
|
|
11585
|
+
"DATE",
|
|
11586
|
+
"DATETIME",
|
|
11587
|
+
"TIME",
|
|
11588
|
+
"CREATED_TIME",
|
|
11589
|
+
"UPDATED_TIME",
|
|
11590
|
+
"DROP_DOWN",
|
|
11591
|
+
"RADIO_BUTTON",
|
|
11592
|
+
"STATUS",
|
|
11593
|
+
"LINK",
|
|
11594
|
+
"CREATOR",
|
|
11595
|
+
"MODIFIER"
|
|
11596
|
+
]);
|
|
11597
|
+
function hasSelectOutputAlias2(stmt, name) {
|
|
11598
|
+
return stmt.columns.some((column) => "alias" in column && column.alias === name);
|
|
11687
11599
|
}
|
|
11688
|
-
function
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11600
|
+
function planKorder(input) {
|
|
11601
|
+
const { stmt } = input;
|
|
11602
|
+
const reasons = [];
|
|
11603
|
+
if (stmt.orderMode !== "KINTONE_NATIVE") reasons.push("KORDER_MODE_REQUIRED");
|
|
11604
|
+
if (stmt.from.cteName !== null || stmt.from.subtableCode || input.staticMode !== "SIMPLE") {
|
|
11605
|
+
reasons.push("KORDER_QUERY_SHAPE_UNSUPPORTED");
|
|
11606
|
+
}
|
|
11607
|
+
if (input.whereCapability !== "EXACT_PUSHDOWN") {
|
|
11608
|
+
reasons.push(...(input.whereReasons ?? []).filter((reason) => reason.functionName !== void 0).map((reason) => reason.code));
|
|
11609
|
+
reasons.push("KORDER_WHERE_NOT_EXACT");
|
|
11610
|
+
}
|
|
11611
|
+
if (input.hasKlike) reasons.push("KORDER_KLIKE_UNSUPPORTED");
|
|
11612
|
+
if (stmt.orderBy.length === 0) reasons.push("KORDER_KEY_REQUIRED");
|
|
11613
|
+
for (const item of stmt.orderBy) {
|
|
11614
|
+
if (item.key.type !== "FIELD_NAME") {
|
|
11615
|
+
reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(key=${item.key.type})`);
|
|
11616
|
+
continue;
|
|
11698
11617
|
}
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
};
|
|
11618
|
+
const name = item.key.name;
|
|
11619
|
+
const semantics = input.orderSemantics.get(name);
|
|
11620
|
+
if (!semantics) {
|
|
11621
|
+
reasons.push(`KORDER_KEY_UNRESOLVED(field=${name})`);
|
|
11622
|
+
continue;
|
|
11705
11623
|
}
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
return column.value;
|
|
11710
|
-
case "AGGREGATE": {
|
|
11711
|
-
const source = aggregateSyntheticName(column.func, column.distinct, column.arg);
|
|
11712
|
-
return row[column.alias ?? source] ?? row[source] ?? "0";
|
|
11713
|
-
}
|
|
11714
|
-
case "ARITH_AGG_COL": {
|
|
11715
|
-
const source = column.alias ?? aggArithDefaultKey(column.expr);
|
|
11716
|
-
return row[source] ?? "0";
|
|
11624
|
+
if (hasSelectOutputAlias2(stmt, name)) {
|
|
11625
|
+
reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(field=${name})`);
|
|
11626
|
+
continue;
|
|
11717
11627
|
}
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
column.expr,
|
|
11723
|
-
row,
|
|
11724
|
-
context.resolveFieldType,
|
|
11725
|
-
context.resolveFieldSemantics
|
|
11726
|
-
);
|
|
11727
|
-
case "GROUPING_COL":
|
|
11728
|
-
return evalGroupingRef(column.ref, row);
|
|
11729
|
-
case "STRFUNC_COL": {
|
|
11730
|
-
const source = stringFuncDefaultKey(column.expr);
|
|
11731
|
-
return hasAggregateInStringFuncExpr2(column.expr) ? row[column.alias ?? source] ?? row[source] ?? evalStringFunc(
|
|
11732
|
-
column.expr,
|
|
11733
|
-
row,
|
|
11734
|
-
context.resolveFieldType,
|
|
11735
|
-
context.resolveFieldSemantics
|
|
11736
|
-
) : evalStringFunc(
|
|
11737
|
-
column.expr,
|
|
11738
|
-
row,
|
|
11739
|
-
context.resolveFieldType,
|
|
11740
|
-
context.resolveFieldSemantics
|
|
11741
|
-
);
|
|
11628
|
+
if (name === "$id") continue;
|
|
11629
|
+
if (!semantics.source || semantics.source.fieldCode !== name) {
|
|
11630
|
+
reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(field=${name})`);
|
|
11631
|
+
continue;
|
|
11742
11632
|
}
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
return scalarValueHasAggregate2(column.expr) ? row[column.alias ?? source] ?? row[source] ?? "" : String(evalScalarValueExpr(
|
|
11746
|
-
column.expr,
|
|
11747
|
-
row,
|
|
11748
|
-
context.resolveFieldType,
|
|
11749
|
-
context.resolveFieldSemantics
|
|
11750
|
-
));
|
|
11633
|
+
if (!KORDER_NATIVE_FIELD_TYPES.has(semantics.fieldType)) {
|
|
11634
|
+
reasons.push(`KORDER_TYPE_UNSUPPORTED(field=${name}, type=${semantics.fieldType})`);
|
|
11751
11635
|
}
|
|
11752
|
-
case "SCALAR_SUBQUERY_COL":
|
|
11753
|
-
return context.scalarCache?.get(columnIndex) ?? "";
|
|
11754
|
-
case "WINDOW_COL":
|
|
11755
|
-
return row[column.alias] ?? "";
|
|
11756
11636
|
}
|
|
11637
|
+
if (stmt.limit === null || !Number.isSafeInteger(stmt.limit) || stmt.limit < 0) {
|
|
11638
|
+
reasons.push(`KORDER_LIMIT_INVALID(limit=${String(stmt.limit)})`);
|
|
11639
|
+
}
|
|
11640
|
+
const offset = stmt.offset ?? 0;
|
|
11641
|
+
if (!Number.isSafeInteger(offset) || offset < 0) {
|
|
11642
|
+
reasons.push(`KORDER_OFFSET_INVALID(offset=${offset})`);
|
|
11643
|
+
}
|
|
11644
|
+
const scanRows = stmt.limit === null ? Number.NaN : offset + stmt.limit;
|
|
11645
|
+
if (stmt.limit !== null && !Number.isSafeInteger(scanRows)) {
|
|
11646
|
+
reasons.push(`KORDER_SCAN_ROWS_INVALID(offset=${offset}, limit=${stmt.limit})`);
|
|
11647
|
+
}
|
|
11648
|
+
const unique2 = [...new Set(reasons)];
|
|
11649
|
+
if (unique2.length > 0) {
|
|
11650
|
+
throw new Error(
|
|
11651
|
+
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique2.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
|
|
11652
|
+
);
|
|
11653
|
+
}
|
|
11654
|
+
const native = stmt.limit <= 500 && offset <= 1e4 && stmt.limit <= input.maxRecords;
|
|
11655
|
+
if (!native && scanRows > input.maxRecords) {
|
|
11656
|
+
throw new Error(
|
|
11657
|
+
`ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; KORDER_SCAN_ROWS_EXCEEDS_MAX_RECORDS(scanRows=${scanRows}, maxRecords=${input.maxRecords})). Use ORDER BY for canonical local ordering, raise maxRecords, or reduce LIMIT/OFFSET.`
|
|
11658
|
+
);
|
|
11659
|
+
}
|
|
11660
|
+
return {
|
|
11661
|
+
kind: native ? "KORDER_NATIVE" : "KORDER_CURSOR",
|
|
11662
|
+
requiresCompleteInput: false,
|
|
11663
|
+
localOrderBy: false,
|
|
11664
|
+
applyLocalOffsetLimit: false,
|
|
11665
|
+
reasonCodes: [],
|
|
11666
|
+
scanRows
|
|
11667
|
+
};
|
|
11757
11668
|
}
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11669
|
+
|
|
11670
|
+
// src/core/errors/cursorErrors.ts
|
|
11671
|
+
var CursorCapacityError = class extends Error {
|
|
11672
|
+
constructor(host, limit, waitMs) {
|
|
11673
|
+
super(`CursorCapacityError: host=${host} \u306E active cursor \u4E0A\u9650 ${limit} \u306B ${waitMs}ms \u4EE5\u5185\u3067\u7A7A\u304D\u304C\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002`);
|
|
11674
|
+
this.name = "CursorCapacityError";
|
|
11675
|
+
}
|
|
11676
|
+
};
|
|
11677
|
+
var CursorCreateOutcomeUnknownError = class extends Error {
|
|
11678
|
+
constructor(cause) {
|
|
11679
|
+
super("CursorCreateOutcomeUnknownError: Create Cursor \u306E\u6210\u5426\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u81EA\u52D5\u518D\u8A66\u884C\u305B\u305A\u3001\u6700\u592710\u5206+\u5B89\u5168\u4F59\u88D5\u306E\u9593\u306F\u67A0\u3092\u9694\u96E2\u3057\u307E\u3059\u3002");
|
|
11680
|
+
this.name = "CursorCreateOutcomeUnknownError";
|
|
11681
|
+
this.cause = cause;
|
|
11682
|
+
}
|
|
11683
|
+
};
|
|
11684
|
+
var CursorCleanupWarning = class extends Error {
|
|
11685
|
+
constructor(cause) {
|
|
11686
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
11687
|
+
super(`CursorCleanupWarning: Cursor \u306E\u89E3\u653E\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u7D50\u679C\u306F\u6709\u52B9\u3067\u3059\u304C\u3001\u6700\u592710\u5206+\u5B89\u5168\u4F59\u88D5\u306E\u9593\u306F\u67A0\u3092\u9694\u96E2\u3057\u307E\u3059\u3002\u8A73\u7D30: ${detail}`);
|
|
11688
|
+
this.name = "CursorCleanupWarning";
|
|
11689
|
+
this.cause = cause;
|
|
11690
|
+
}
|
|
11691
|
+
};
|
|
11692
|
+
|
|
11693
|
+
// src/core/optimization/korderCursorExecutor.ts
|
|
11694
|
+
async function executeKorderCursor(input) {
|
|
11695
|
+
const handle = await input.client.openCursor({
|
|
11696
|
+
app: input.app,
|
|
11697
|
+
fields: input.fields.length > 0 ? input.fields : void 0,
|
|
11698
|
+
query: input.query,
|
|
11699
|
+
size: 500
|
|
11762
11700
|
});
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
if (typeof value !== "string") {
|
|
11776
|
-
for (const [key, entryValue] of value.entries) {
|
|
11777
|
-
if (entryValue !== null) out[key] = entryValue;
|
|
11701
|
+
const records = [];
|
|
11702
|
+
let seen = 0;
|
|
11703
|
+
let primaryError;
|
|
11704
|
+
let cleanupWarning;
|
|
11705
|
+
try {
|
|
11706
|
+
if (handle.totalCount > input.offset) {
|
|
11707
|
+
while (records.length < input.limit) {
|
|
11708
|
+
const page = await handle.nextPage();
|
|
11709
|
+
for (const record of page.records) {
|
|
11710
|
+
if (seen < input.offset) seen += 1;
|
|
11711
|
+
else if (records.length < input.limit) records.push(record);
|
|
11712
|
+
else break;
|
|
11778
11713
|
}
|
|
11714
|
+
if (!page.next) break;
|
|
11779
11715
|
}
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
|
|
11716
|
+
}
|
|
11717
|
+
} catch (error) {
|
|
11718
|
+
primaryError = error;
|
|
11719
|
+
throw error;
|
|
11720
|
+
} finally {
|
|
11721
|
+
try {
|
|
11722
|
+
await handle.close();
|
|
11723
|
+
} catch (cleanupError) {
|
|
11724
|
+
if (primaryError && primaryError instanceof Error) {
|
|
11725
|
+
Object.defineProperty(primaryError, "cursorCleanupError", {
|
|
11726
|
+
value: cleanupError,
|
|
11727
|
+
configurable: true
|
|
11728
|
+
});
|
|
11729
|
+
} else {
|
|
11730
|
+
cleanupWarning = new CursorCleanupWarning(cleanupError).message;
|
|
11731
|
+
}
|
|
11732
|
+
}
|
|
11784
11733
|
}
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
const
|
|
11791
|
-
if (
|
|
11792
|
-
|
|
11734
|
+
return { records, cleanupWarning };
|
|
11735
|
+
}
|
|
11736
|
+
|
|
11737
|
+
// src/converter/korderCursorQuery.ts
|
|
11738
|
+
function buildKorderCursorQuery(stmt) {
|
|
11739
|
+
const parts = [];
|
|
11740
|
+
if (stmt.where) parts.push(whereToKintone(stmt.where));
|
|
11741
|
+
const order = stmt.orderBy.map((item) => {
|
|
11742
|
+
if (item.key.type !== "FIELD_NAME") {
|
|
11743
|
+
throw new Error("ArgumentError: KORDER cursor key must be a direct field.");
|
|
11744
|
+
}
|
|
11745
|
+
return `${item.key.name} ${item.direction === "ASC" ? "asc" : "desc"}`;
|
|
11746
|
+
});
|
|
11747
|
+
parts.push(`order by ${order.join(", ")}`);
|
|
11748
|
+
return parts.join(" ");
|
|
11749
|
+
}
|
|
11750
|
+
|
|
11751
|
+
// src/core/systemFields.ts
|
|
11752
|
+
var APP_SYSTEM_FIELD_CODES = [
|
|
11753
|
+
"$id",
|
|
11754
|
+
"$revision",
|
|
11755
|
+
"\u30EC\u30B3\u30FC\u30C9\u756A\u53F7",
|
|
11756
|
+
"\u4F5C\u6210\u8005",
|
|
11757
|
+
"\u4F5C\u6210\u65E5\u6642",
|
|
11758
|
+
"\u66F4\u65B0\u8005",
|
|
11759
|
+
"\u66F4\u65B0\u65E5\u6642",
|
|
11760
|
+
"\u30B9\u30C6\u30FC\u30BF\u30B9",
|
|
11761
|
+
"\u4F5C\u696D\u8005"
|
|
11762
|
+
];
|
|
11763
|
+
function isSystemLikeFieldCode(code) {
|
|
11764
|
+
return code.startsWith("_") || code.startsWith("$");
|
|
11765
|
+
}
|
|
11766
|
+
|
|
11767
|
+
// src/core/optimization/plainGroupByPlan.ts
|
|
11768
|
+
var AGGREGATE_REFERENCE_PREFIX = /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/;
|
|
11769
|
+
function containsAggregateColumnNode(node) {
|
|
11770
|
+
if (node === null || typeof node !== "object") return false;
|
|
11771
|
+
if (Array.isArray(node)) return node.some(containsAggregateColumnNode);
|
|
11772
|
+
const value = node;
|
|
11773
|
+
if (value["type"] === "AGGREGATE" || value["type"] === "ARITH_AGG_COL") return true;
|
|
11774
|
+
if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
|
|
11775
|
+
if (value["type"] === "FIELD" && typeof value["field"] === "string") {
|
|
11776
|
+
return AGGREGATE_REFERENCE_PREFIX.test(value["field"]);
|
|
11793
11777
|
}
|
|
11794
|
-
|
|
11795
|
-
const out = {};
|
|
11796
|
-
const evaluationContext = {
|
|
11797
|
-
scalarCache,
|
|
11798
|
-
resolveFieldType,
|
|
11799
|
-
resolveFieldSemantics: resolveFieldSemantics2,
|
|
11800
|
-
wildcardKeys: Object.keys(stripHiddenQualifiedColumns(
|
|
11801
|
-
stripParentShortcutColumns(row),
|
|
11802
|
-
hiddenQualifiedAliases
|
|
11803
|
-
)),
|
|
11804
|
-
parentWildcardKeys: Object.keys(row).filter((key) => key.startsWith("_p.")).sort()
|
|
11805
|
-
};
|
|
11806
|
-
for (const [colIdx, col] of columns.entries()) {
|
|
11807
|
-
const value = evaluateSelectColumnValue(col, row, colIdx, evaluationContext);
|
|
11808
|
-
switch (col.type) {
|
|
11809
|
-
case "VARIABLE_COL":
|
|
11810
|
-
break;
|
|
11811
|
-
case "WILDCARD":
|
|
11812
|
-
if (typeof value !== "string") {
|
|
11813
|
-
for (const [key, entryValue] of value.entries) {
|
|
11814
|
-
if (entryValue !== null) out[key] = entryValue;
|
|
11815
|
-
}
|
|
11816
|
-
}
|
|
11817
|
-
break;
|
|
11818
|
-
case "PARENT_WILDCARD": {
|
|
11819
|
-
if (typeof value !== "string") {
|
|
11820
|
-
for (const [key, entryValue] of value.entries) {
|
|
11821
|
-
if (entryValue !== null) out[key] = entryValue;
|
|
11822
|
-
if (rowIdx === 0) orderedKeys.push(key);
|
|
11823
|
-
}
|
|
11824
|
-
}
|
|
11825
|
-
break;
|
|
11826
|
-
}
|
|
11827
|
-
case "FIELD": {
|
|
11828
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
11829
|
-
out[key] = value;
|
|
11830
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11831
|
-
break;
|
|
11832
|
-
}
|
|
11833
|
-
case "LITERAL_COL": {
|
|
11834
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? `'${col.value}'`;
|
|
11835
|
-
out[key] = value;
|
|
11836
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11837
|
-
break;
|
|
11838
|
-
}
|
|
11839
|
-
case "AGGREGATE": {
|
|
11840
|
-
const srcKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
|
|
11841
|
-
const dstKey = outputKeys?.[colIdx] ?? col.alias ?? srcKey;
|
|
11842
|
-
out[dstKey] = value;
|
|
11843
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(dstKey);
|
|
11844
|
-
break;
|
|
11845
|
-
}
|
|
11846
|
-
case "ARITH_AGG_COL": {
|
|
11847
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? aggArithDefaultKey(col.expr);
|
|
11848
|
-
out[key] = value;
|
|
11849
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11850
|
-
break;
|
|
11851
|
-
}
|
|
11852
|
-
case "ARITH_COL": {
|
|
11853
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? arithColDefaultKey(col.expr);
|
|
11854
|
-
out[key] = value;
|
|
11855
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11856
|
-
break;
|
|
11857
|
-
}
|
|
11858
|
-
case "CASE_COL": {
|
|
11859
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
11860
|
-
out[key] = value;
|
|
11861
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11862
|
-
break;
|
|
11863
|
-
}
|
|
11864
|
-
case "GROUPING_COL": {
|
|
11865
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? `GROUPING(${col.ref.field.tableAlias ? `${col.ref.field.tableAlias}.` : ""}${col.ref.field.field})`;
|
|
11866
|
-
out[key] = value;
|
|
11867
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11868
|
-
break;
|
|
11869
|
-
}
|
|
11870
|
-
case "STRFUNC_COL": {
|
|
11871
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? stringFuncDefaultKey(col.expr);
|
|
11872
|
-
out[key] = value;
|
|
11873
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11874
|
-
break;
|
|
11875
|
-
}
|
|
11876
|
-
case "SCALAR_VALUE_COL": {
|
|
11877
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? scalarValueDefaultKey(col.expr);
|
|
11878
|
-
out[key] = value;
|
|
11879
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11880
|
-
break;
|
|
11881
|
-
}
|
|
11882
|
-
case "SCALAR_SUBQUERY_COL": {
|
|
11883
|
-
const key = outputKeys?.[colIdx] ?? col.alias ?? "(subquery)";
|
|
11884
|
-
out[key] = value;
|
|
11885
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11886
|
-
break;
|
|
11887
|
-
}
|
|
11888
|
-
case "WINDOW_COL": {
|
|
11889
|
-
const key = outputKeys?.[colIdx] ?? col.alias;
|
|
11890
|
-
out[key] = value;
|
|
11891
|
-
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
11892
|
-
break;
|
|
11893
|
-
}
|
|
11894
|
-
}
|
|
11895
|
-
}
|
|
11896
|
-
return out;
|
|
11897
|
-
});
|
|
11898
|
-
return { rows: projected, columns: orderedKeys };
|
|
11899
|
-
}
|
|
11900
|
-
function computeOutputKeys(columns, defaultFieldKeys) {
|
|
11901
|
-
return columns.map((col, colIdx) => computeOutputKey(col, colIdx, defaultFieldKeys));
|
|
11902
|
-
}
|
|
11903
|
-
function computeExplicitOutputKeys(columns, defaultFieldKeys) {
|
|
11904
|
-
const keys = [];
|
|
11905
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11906
|
-
for (const [colIdx, col] of columns.entries()) {
|
|
11907
|
-
if (col.type === "WILDCARD" || col.type === "PARENT_WILDCARD") continue;
|
|
11908
|
-
const key = computeOutputKey(col, colIdx, defaultFieldKeys);
|
|
11909
|
-
if (!seen.has(key)) {
|
|
11910
|
-
seen.add(key);
|
|
11911
|
-
keys.push(key);
|
|
11912
|
-
}
|
|
11913
|
-
}
|
|
11914
|
-
return keys;
|
|
11778
|
+
return Object.values(value).some(containsAggregateColumnNode);
|
|
11915
11779
|
}
|
|
11916
|
-
function
|
|
11917
|
-
switch (
|
|
11780
|
+
function classifyPreGroupAlias(column) {
|
|
11781
|
+
switch (column.type) {
|
|
11782
|
+
case "AGGREGATE":
|
|
11783
|
+
case "ARITH_AGG_COL":
|
|
11784
|
+
return "AGGREGATE_DEPENDENT";
|
|
11785
|
+
case "GROUPING_COL":
|
|
11786
|
+
case "WINDOW_COL":
|
|
11787
|
+
return "POST_GROUP_ONLY";
|
|
11918
11788
|
case "VARIABLE_COL":
|
|
11919
|
-
throw new Error(
|
|
11789
|
+
throw new Error("InternalError: unresolved VARIABLE_COL reached GROUP BY alias planning.");
|
|
11790
|
+
case "WILDCARD":
|
|
11791
|
+
case "PARENT_WILDCARD":
|
|
11920
11792
|
case "FIELD":
|
|
11921
|
-
return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
11922
11793
|
case "LITERAL_COL":
|
|
11923
|
-
return col.alias ?? `'${col.value}'`;
|
|
11924
|
-
case "AGGREGATE":
|
|
11925
|
-
return col.alias ?? aggregateSyntheticName(col.func, col.distinct, col.arg);
|
|
11926
|
-
case "ARITH_AGG_COL":
|
|
11927
|
-
return col.alias ?? aggArithDefaultKey(col.expr);
|
|
11928
11794
|
case "ARITH_COL":
|
|
11929
|
-
return col.alias ?? arithColDefaultKey(col.expr);
|
|
11930
11795
|
case "CASE_COL":
|
|
11931
|
-
return col.alias ?? "case";
|
|
11932
11796
|
case "STRFUNC_COL":
|
|
11933
|
-
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
11934
11797
|
case "SCALAR_VALUE_COL":
|
|
11935
|
-
return col.alias ?? scalarValueDefaultKey(col.expr);
|
|
11936
|
-
case "GROUPING_COL":
|
|
11937
|
-
return col.alias ?? `GROUPING(${col.ref.field.tableAlias ? `${col.ref.field.tableAlias}.` : ""}${col.ref.field.field})`;
|
|
11938
11798
|
case "SCALAR_SUBQUERY_COL":
|
|
11939
|
-
return
|
|
11940
|
-
case "WINDOW_COL":
|
|
11941
|
-
return col.alias;
|
|
11942
|
-
case "WILDCARD":
|
|
11943
|
-
case "PARENT_WILDCARD":
|
|
11944
|
-
throw new Error("internal: computeOutputKey received a wildcard column");
|
|
11945
|
-
}
|
|
11946
|
-
}
|
|
11947
|
-
function buildDefaultFieldOutputKeys(columns) {
|
|
11948
|
-
const qualifierCollisionCount = /* @__PURE__ */ new Map();
|
|
11949
|
-
for (const col of columns) {
|
|
11950
|
-
if (col.type !== "FIELD" || col.alias) continue;
|
|
11951
|
-
const unqualified = stripTableQualifier(col.field);
|
|
11952
|
-
qualifierCollisionCount.set(unqualified, (qualifierCollisionCount.get(unqualified) ?? 0) + 1);
|
|
11953
|
-
}
|
|
11954
|
-
const keys = /* @__PURE__ */ new Map();
|
|
11955
|
-
for (const [idx, col] of columns.entries()) {
|
|
11956
|
-
if (col.type !== "FIELD" || col.alias) continue;
|
|
11957
|
-
const unqualified = stripTableQualifier(col.field);
|
|
11958
|
-
const duplicate = (qualifierCollisionCount.get(unqualified) ?? 0) > 1;
|
|
11959
|
-
const hasTableQualifier = col.field.includes(".") && !col.field.startsWith("_p.");
|
|
11960
|
-
keys.set(idx, duplicate && hasTableQualifier ? col.field : unqualified);
|
|
11961
|
-
}
|
|
11962
|
-
return keys;
|
|
11963
|
-
}
|
|
11964
|
-
function stripTableQualifier(field) {
|
|
11965
|
-
if (field.startsWith("_p.")) return field;
|
|
11966
|
-
const dot = field.indexOf(".");
|
|
11967
|
-
if (dot <= 0) return field;
|
|
11968
|
-
const unqualified = field.slice(dot + 1);
|
|
11969
|
-
return unqualified || field;
|
|
11970
|
-
}
|
|
11971
|
-
function stripParentShortcutColumns(row) {
|
|
11972
|
-
const out = {};
|
|
11973
|
-
for (const [k, v] of Object.entries(row)) {
|
|
11974
|
-
if (k.startsWith("_p.")) continue;
|
|
11975
|
-
out[k] = v;
|
|
11976
|
-
}
|
|
11977
|
-
return out;
|
|
11978
|
-
}
|
|
11979
|
-
function arithColDefaultKey(expr) {
|
|
11980
|
-
const nodeLabel = (n) => {
|
|
11981
|
-
if (n.type === "FIELD_REF") return n.field;
|
|
11982
|
-
if (n.type === "NUMBER") return numberLiteralText(n);
|
|
11983
|
-
if (n.type === "STRING_FUNC") return stringFuncDefaultKey(n);
|
|
11984
|
-
return `(${nodeLabel(n.left)}${n.op}${nodeLabel(n.right)})`;
|
|
11985
|
-
};
|
|
11986
|
-
if (expr.type === "ARITH") {
|
|
11987
|
-
return `${nodeLabel(expr.left)}${expr.op}${nodeLabel(expr.right)}`;
|
|
11799
|
+
return containsAggregate(column) || containsAggregateColumnNode(column) ? "AGGREGATE_DEPENDENT" : "SAFE";
|
|
11988
11800
|
}
|
|
11989
|
-
return nodeLabel(expr);
|
|
11990
11801
|
}
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
return scalarValueDefaultKey(a);
|
|
11995
|
-
});
|
|
11996
|
-
return `${expr.func}(${argStrs.join(",")})`;
|
|
11802
|
+
var SUBTABLE_SYSTEM_COLUMNS = ["_pid", "_rid", "_idx"];
|
|
11803
|
+
function unique(values) {
|
|
11804
|
+
return [...new Set(values)];
|
|
11997
11805
|
}
|
|
11998
|
-
function
|
|
11999
|
-
switch (
|
|
12000
|
-
case "
|
|
12001
|
-
return
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
return `${scalarValueDefaultKey(expr.left)}${expr.op}${scalarValueDefaultKey(expr.right)}`;
|
|
12014
|
-
case "CONCAT_OP":
|
|
12015
|
-
return `${scalarValueDefaultKey(expr.left)}||${scalarValueDefaultKey(expr.right)}`;
|
|
11806
|
+
function sourceColumns(input) {
|
|
11807
|
+
switch (input.kind) {
|
|
11808
|
+
case "APP":
|
|
11809
|
+
return unique([
|
|
11810
|
+
...input.fieldCodes,
|
|
11811
|
+
...APP_SYSTEM_FIELD_CODES
|
|
11812
|
+
]);
|
|
11813
|
+
case "SUBTABLE":
|
|
11814
|
+
return unique([
|
|
11815
|
+
...input.childFieldCodes,
|
|
11816
|
+
...SUBTABLE_SYSTEM_COLUMNS,
|
|
11817
|
+
...input.parentFieldCodes.map((field) => `_p.${field}`)
|
|
11818
|
+
]);
|
|
11819
|
+
case "MATERIALIZED":
|
|
11820
|
+
return unique(input.columns);
|
|
12016
11821
|
}
|
|
12017
11822
|
}
|
|
12018
|
-
function
|
|
12019
|
-
|
|
12020
|
-
return
|
|
11823
|
+
function resolvePlainGroupBySourceSchemas(stmt, lookup) {
|
|
11824
|
+
const sources = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
11825
|
+
return sources.map((source, sourceIndex) => ({
|
|
11826
|
+
sourceIndex,
|
|
11827
|
+
qualifier: source.alias ?? source.cteName,
|
|
11828
|
+
columns: sourceColumns(lookup(source, sourceIndex))
|
|
11829
|
+
}));
|
|
12021
11830
|
}
|
|
12022
|
-
function
|
|
12023
|
-
if (
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
if (expr.type === "CASE_WHEN") {
|
|
12028
|
-
return expr.branches.some((branch) => caseResultHasAggregate2(branch.result)) || expr.elseResult !== null && caseResultHasAggregate2(expr.elseResult);
|
|
11831
|
+
function parseGroupName(name) {
|
|
11832
|
+
if (name.startsWith("_p.")) return { qualifier: null, fieldCode: name };
|
|
11833
|
+
const dot = name.indexOf(".");
|
|
11834
|
+
if (dot <= 0 || dot === name.length - 1) {
|
|
11835
|
+
return { qualifier: null, fieldCode: name };
|
|
12029
11836
|
}
|
|
12030
|
-
return
|
|
11837
|
+
return { qualifier: name.slice(0, dot), fieldCode: name.slice(dot + 1) };
|
|
12031
11838
|
}
|
|
12032
|
-
function
|
|
12033
|
-
|
|
12034
|
-
return scalarValueHasAggregate2(result);
|
|
11839
|
+
function runtimeKey(source, fieldCode) {
|
|
11840
|
+
return source.qualifier === null ? fieldCode : `${source.qualifier}.${fieldCode}`;
|
|
12035
11841
|
}
|
|
12036
|
-
function
|
|
12037
|
-
return
|
|
11842
|
+
function explicitAlias(column) {
|
|
11843
|
+
return "alias" in column && typeof column.alias === "string" ? column.alias : null;
|
|
12038
11844
|
}
|
|
12039
|
-
function
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
11845
|
+
function resolveFieldName(name, columns, schemas) {
|
|
11846
|
+
const parsed = parseGroupName(name);
|
|
11847
|
+
const candidateSources = parsed.qualifier === null ? schemas : schemas.filter((source) => source.qualifier === parsed.qualifier);
|
|
11848
|
+
const physical = candidateSources.filter((source) => source.columns.includes(parsed.fieldCode));
|
|
11849
|
+
if (physical.length > 1) {
|
|
11850
|
+
throw new Error(
|
|
11851
|
+
`ArgumentError: GROUP BY field ${name} is ambiguous across multiple sources (reason=GROUP_BY_FIELD_AMBIGUOUS).`
|
|
11852
|
+
);
|
|
12047
11853
|
}
|
|
12048
|
-
if (
|
|
12049
|
-
|
|
11854
|
+
if (physical.length === 1) {
|
|
11855
|
+
const source = physical[0];
|
|
11856
|
+
return {
|
|
11857
|
+
kind: "PHYSICAL",
|
|
11858
|
+
sourceIndex: source.sourceIndex,
|
|
11859
|
+
fieldCode: parsed.fieldCode,
|
|
11860
|
+
runtimeKey: runtimeKey(source, parsed.fieldCode)
|
|
11861
|
+
};
|
|
12050
11862
|
}
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
if (
|
|
11863
|
+
if (parsed.qualifier !== null) return { kind: "UNKNOWN", name };
|
|
11864
|
+
const aliases = columns.flatMap(
|
|
11865
|
+
(column, columnIndex) => explicitAlias(column) === name ? [{ column, columnIndex }] : []
|
|
11866
|
+
);
|
|
11867
|
+
if (aliases.length > 1) return { kind: "ALIAS_REJECT", reason: "DUPLICATE" };
|
|
11868
|
+
if (aliases.length === 1) {
|
|
11869
|
+
const candidate = aliases[0];
|
|
11870
|
+
const classification = classifyPreGroupAlias(candidate.column);
|
|
11871
|
+
if (classification === "SAFE") {
|
|
11872
|
+
return { kind: "ALIAS_SAFE", columnIndex: candidate.columnIndex };
|
|
11873
|
+
}
|
|
12056
11874
|
return {
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
right: resolveAggInScalarValue(expr.right, rows, resolveAggSortKind)
|
|
11875
|
+
kind: "ALIAS_REJECT",
|
|
11876
|
+
reason: classification === "AGGREGATE_DEPENDENT" ? "AGGREGATE" : "POST_GROUP_ONLY"
|
|
12060
11877
|
};
|
|
12061
11878
|
}
|
|
12062
|
-
|
|
11879
|
+
const aggregateSyntheticMatch = columns.some(
|
|
11880
|
+
(column) => column.type === "AGGREGATE" && column.alias === null && aggregateSyntheticName(column.func, column.distinct, column.arg) === name
|
|
11881
|
+
);
|
|
11882
|
+
if (aggregateSyntheticMatch) return { kind: "ALIAS_REJECT", reason: "AGGREGATE" };
|
|
11883
|
+
return { kind: "UNKNOWN", name };
|
|
12063
11884
|
}
|
|
12064
|
-
function
|
|
11885
|
+
function planPlainGroupByResolution(groupBy, columns, schemas) {
|
|
12065
11886
|
return {
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
11887
|
+
items: groupBy.map(
|
|
11888
|
+
(key) => key.type === "FIELD_NAME" ? resolveFieldName(key.name, columns, schemas) : { kind: "EXPRESSION" }
|
|
11889
|
+
)
|
|
12069
11890
|
};
|
|
12070
11891
|
}
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
11892
|
+
|
|
11893
|
+
// src/engine/process.ts
|
|
11894
|
+
function flatten(record, alias) {
|
|
11895
|
+
const row = {};
|
|
11896
|
+
for (const [field, fv] of Object.entries(record)) {
|
|
11897
|
+
const val = fv.value;
|
|
11898
|
+
const strVal = val == null ? "" : typeof val === "string" ? val : JSON.stringify(val);
|
|
11899
|
+
if (alias) {
|
|
11900
|
+
row[`${alias}.${field}`] = strVal;
|
|
11901
|
+
row[field] = strVal;
|
|
11902
|
+
} else {
|
|
11903
|
+
row[field] = strVal;
|
|
11904
|
+
}
|
|
12077
11905
|
}
|
|
12078
|
-
return
|
|
12079
|
-
}
|
|
12080
|
-
function flattenedColumns(columns, alias) {
|
|
12081
|
-
if (columns === void 0) return void 0;
|
|
12082
|
-
return alias ? columns.flatMap((column) => [`${alias}.${column}`, column]) : [...columns];
|
|
12083
|
-
}
|
|
12084
|
-
function mergeKnownColumns(left, right, rows) {
|
|
12085
|
-
if (left === void 0 && right === void 0 && rows.length === 0) return void 0;
|
|
12086
|
-
return [.../* @__PURE__ */ new Set([
|
|
12087
|
-
...left ?? [],
|
|
12088
|
-
...right ?? [],
|
|
12089
|
-
...Object.keys(rows[0] ?? {})
|
|
12090
|
-
])];
|
|
11906
|
+
return row;
|
|
12091
11907
|
}
|
|
12092
|
-
function
|
|
12093
|
-
const
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
11908
|
+
function applyJoin(leftRows, rightRows, join2, columns = {}) {
|
|
11909
|
+
const { on, type: joinType } = join2;
|
|
11910
|
+
const leftKey = on.left.tableAlias ? `${on.left.tableAlias}.${on.left.field}` : on.left.field;
|
|
11911
|
+
const rightKey = on.right.tableAlias ? `${on.right.tableAlias}.${on.right.field}` : on.right.field;
|
|
11912
|
+
assertJoinKeyAvailable(leftRows, leftKey, columns.leftColumns);
|
|
11913
|
+
assertJoinKeyAvailable(rightRows, rightKey, columns.rightColumns);
|
|
11914
|
+
if (joinType === "RIGHT") {
|
|
11915
|
+
const leftIndex = /* @__PURE__ */ new Map();
|
|
11916
|
+
for (const lRow of leftRows) {
|
|
11917
|
+
const k = lRow[leftKey] ?? "";
|
|
11918
|
+
const bucket = leftIndex.get(k);
|
|
11919
|
+
if (bucket) bucket.push(lRow);
|
|
11920
|
+
else leftIndex.set(k, [lRow]);
|
|
11921
|
+
}
|
|
11922
|
+
const emptyLeft = {};
|
|
11923
|
+
for (const key of columns.leftColumns ?? Object.keys(leftRows[0] ?? {})) emptyLeft[key] = "";
|
|
11924
|
+
const result2 = [];
|
|
11925
|
+
for (const rRow of rightRows) {
|
|
11926
|
+
const k = rRow[rightKey] ?? "";
|
|
11927
|
+
const matched = leftIndex.get(k) ?? [];
|
|
11928
|
+
if (matched.length > 0) {
|
|
11929
|
+
for (const lRow of matched) result2.push({ ...lRow, ...rRow });
|
|
11930
|
+
} else {
|
|
11931
|
+
result2.push({ ...emptyLeft, ...rRow });
|
|
12103
11932
|
}
|
|
12104
|
-
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
12105
|
-
result.set(column.alias, syntheticSemantics("string"));
|
|
12106
|
-
} else if (column.type === "STRFUNC_COL") {
|
|
12107
|
-
result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
|
|
12108
11933
|
}
|
|
11934
|
+
return result2;
|
|
12109
11935
|
}
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
scalarCache,
|
|
12117
|
-
optionOrders,
|
|
12118
|
-
sortKinds,
|
|
12119
|
-
orderSemantics,
|
|
12120
|
-
fieldTypeResolver,
|
|
12121
|
-
fieldSemanticsResolver,
|
|
12122
|
-
havingFieldTypeResolver,
|
|
12123
|
-
havingFieldSemanticsResolver,
|
|
12124
|
-
aggregateSortKindResolver,
|
|
12125
|
-
appliedKlikes,
|
|
12126
|
-
sourceColumns: sourceColumns2,
|
|
12127
|
-
tableColumns,
|
|
12128
|
-
hiddenQualifiedAliases,
|
|
12129
|
-
resolvedGroupingSpec,
|
|
12130
|
-
plainGroupByPlan
|
|
12131
|
-
} = input;
|
|
12132
|
-
const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
|
|
12133
|
-
for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
|
|
12134
|
-
let rows = [];
|
|
12135
|
-
const mainAlias = stmt.from.alias;
|
|
12136
|
-
const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
|
|
12137
|
-
rows = mainRecords.map((r) => flatten(r, mainAlias));
|
|
12138
|
-
let knownColumns = mergeKnownColumns(
|
|
12139
|
-
flattenedColumns(tableColumns?.get(mainAlias), mainAlias),
|
|
12140
|
-
void 0,
|
|
12141
|
-
rows
|
|
12142
|
-
);
|
|
12143
|
-
for (const join2 of stmt.joins) {
|
|
12144
|
-
const rightAlias = join2.table.alias;
|
|
12145
|
-
const rightRecords = tables.get(rightAlias) ?? [];
|
|
12146
|
-
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
12147
|
-
const rightColumns = mergeKnownColumns(
|
|
12148
|
-
flattenedColumns(tableColumns?.get(rightAlias), rightAlias),
|
|
12149
|
-
void 0,
|
|
12150
|
-
rightRows
|
|
12151
|
-
);
|
|
12152
|
-
rows = applyJoin(rows, rightRows, join2, {
|
|
12153
|
-
leftColumns: knownColumns,
|
|
12154
|
-
rightColumns
|
|
12155
|
-
});
|
|
12156
|
-
knownColumns = mergeKnownColumns(knownColumns, rightColumns, rows);
|
|
11936
|
+
const rightIndex = /* @__PURE__ */ new Map();
|
|
11937
|
+
for (const rRow of rightRows) {
|
|
11938
|
+
const k = rRow[rightKey] ?? "";
|
|
11939
|
+
const bucket = rightIndex.get(k);
|
|
11940
|
+
if (bucket) bucket.push(rRow);
|
|
11941
|
+
else rightIndex.set(k, [rRow]);
|
|
12157
11942
|
}
|
|
12158
|
-
const
|
|
12159
|
-
|
|
12160
|
-
const
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
resolvedGroupingSpec,
|
|
12168
|
-
stmt.columns,
|
|
12169
|
-
aggregateSortKindResolver,
|
|
12170
|
-
{ maxGeneratedRows: B65_MAX_GENERATED_ROWS }
|
|
12171
|
-
);
|
|
12172
|
-
} else if (grouping.type === "PLAIN" || hasAggregateColumns(stmt.columns)) {
|
|
12173
|
-
rows = applyGroupBy(
|
|
12174
|
-
rows,
|
|
12175
|
-
grouping.type === "PLAIN" ? grouping.allItems : [],
|
|
12176
|
-
stmt.columns,
|
|
12177
|
-
aggregateSortKindResolver,
|
|
12178
|
-
grouping.type === "PLAIN" ? plainGroupByPlan : void 0,
|
|
12179
|
-
{
|
|
12180
|
-
scalarCache,
|
|
12181
|
-
resolveFieldType: fieldTypeResolver,
|
|
12182
|
-
resolveFieldSemantics: fieldSemanticsResolver
|
|
11943
|
+
const result = [];
|
|
11944
|
+
const emptyRight = {};
|
|
11945
|
+
for (const key of columns.rightColumns ?? Object.keys(rightRows[0] ?? {})) emptyRight[key] = "";
|
|
11946
|
+
for (const lRow of leftRows) {
|
|
11947
|
+
const k = lRow[leftKey] ?? "";
|
|
11948
|
+
const matched = rightIndex.get(k) ?? [];
|
|
11949
|
+
if (matched.length > 0) {
|
|
11950
|
+
for (const rRow of matched) {
|
|
11951
|
+
result.push({ ...lRow, ...rRow });
|
|
12183
11952
|
}
|
|
12184
|
-
)
|
|
11953
|
+
} else if (joinType === "LEFT") {
|
|
11954
|
+
result.push({ ...lRow, ...emptyRight });
|
|
11955
|
+
}
|
|
12185
11956
|
}
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
scalarCache,
|
|
12193
|
-
fieldTypeResolver,
|
|
12194
|
-
fieldSemanticsResolver
|
|
12195
|
-
);
|
|
11957
|
+
return result;
|
|
11958
|
+
}
|
|
11959
|
+
function assertJoinKeyAvailable(rows, key, savedColumns) {
|
|
11960
|
+
const missing = rows.length > 0 ? rows.some((row) => !Object.prototype.hasOwnProperty.call(row, key)) : savedColumns !== void 0 && !savedColumns.includes(key);
|
|
11961
|
+
if (missing) {
|
|
11962
|
+
throw new Error(`ArgumentError: JOIN key ${key} is not available in the materialized table.`);
|
|
12196
11963
|
}
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
12206
|
-
return project(
|
|
12207
|
-
rows,
|
|
12208
|
-
stmt.columns,
|
|
12209
|
-
scalarCache,
|
|
12210
|
-
fieldTypeResolver,
|
|
12211
|
-
sourceColumns2,
|
|
12212
|
-
fieldSemanticsResolver,
|
|
12213
|
-
hiddenQualifiedAliases
|
|
11964
|
+
}
|
|
11965
|
+
function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
|
|
11966
|
+
if (where === null) return rows;
|
|
11967
|
+
return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
|
|
11968
|
+
}
|
|
11969
|
+
function hasAggregateColumns(columns) {
|
|
11970
|
+
return columns.some(
|
|
11971
|
+
(c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr) || c.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(c.expr)
|
|
12214
11972
|
);
|
|
12215
11973
|
}
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
const
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12228
|
-
|
|
12229
|
-
|
|
12230
|
-
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
11974
|
+
function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind, resolutionPlan, aliasEvaluationContext = {}) {
|
|
11975
|
+
if (resolutionPlan && resolutionPlan.items.length !== groupByKeys.length) {
|
|
11976
|
+
throw new Error("InternalError: plain GROUP BY resolution plan length does not match group keys.");
|
|
11977
|
+
}
|
|
11978
|
+
const groups = /* @__PURE__ */ new Map();
|
|
11979
|
+
for (const row of rows) {
|
|
11980
|
+
const key = groupByKeys.map(
|
|
11981
|
+
(groupKey, index) => evalGroupByKey(
|
|
11982
|
+
groupKey,
|
|
11983
|
+
row,
|
|
11984
|
+
resolutionPlan?.items[index],
|
|
11985
|
+
columns,
|
|
11986
|
+
aliasEvaluationContext
|
|
11987
|
+
)
|
|
11988
|
+
).join("\0");
|
|
11989
|
+
const bucket = groups.get(key);
|
|
11990
|
+
if (bucket) bucket.push(row);
|
|
11991
|
+
else groups.set(key, [row]);
|
|
11992
|
+
}
|
|
11993
|
+
if (groups.size === 0 && groupByKeys.length === 0 && hasAggregateColumns(columns)) {
|
|
11994
|
+
groups.set("", []);
|
|
11995
|
+
}
|
|
11996
|
+
const result = [];
|
|
11997
|
+
for (const groupRows of groups.values()) {
|
|
11998
|
+
const outRow = { ...groupRows[0] };
|
|
11999
|
+
for (const k of groupByKeys) {
|
|
12000
|
+
if (k.type === "ARITH_KEY") {
|
|
12001
|
+
outRow[arithColDefaultKey(k.expr)] = String(evalArithExpr(k.expr, groupRows[0]));
|
|
12002
|
+
} else if (k.type === "FUNC_KEY") {
|
|
12003
|
+
outRow[stringFuncDefaultKey(k.expr)] = evalStringFunc(k.expr, groupRows[0]);
|
|
12238
12004
|
}
|
|
12239
|
-
rows.push(out);
|
|
12240
12005
|
}
|
|
12006
|
+
materializeAggregateColumns(outRow, groupRows, columns, resolveAggSortKind);
|
|
12007
|
+
result.push(outRow);
|
|
12241
12008
|
}
|
|
12242
|
-
return
|
|
12243
|
-
}
|
|
12244
|
-
function readTableRows(value) {
|
|
12245
|
-
if (!Array.isArray(value)) return [];
|
|
12246
|
-
return value.filter((item) => !!item && typeof item === "object");
|
|
12009
|
+
return result;
|
|
12247
12010
|
}
|
|
12248
|
-
function
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12011
|
+
function applyGroupingSets(rows, spec, columns, resolveAggSortKind, limits = {}) {
|
|
12012
|
+
const result = [];
|
|
12013
|
+
let generatedRows = 0;
|
|
12014
|
+
const countBucket = () => {
|
|
12015
|
+
generatedRows++;
|
|
12016
|
+
if (limits.maxGeneratedRows !== void 0 && generatedRows > limits.maxGeneratedRows) {
|
|
12017
|
+
throw new Error(
|
|
12018
|
+
`LimitError: B65 generated grouping rows ${generatedRows} exceed limit ${limits.maxGeneratedRows} (reason=GROUPING_OUTPUT_LIMIT_EXCEEDED).`
|
|
12019
|
+
);
|
|
12020
|
+
}
|
|
12021
|
+
};
|
|
12022
|
+
for (const set of spec.sets) {
|
|
12023
|
+
const root = { children: /* @__PURE__ */ new Map() };
|
|
12024
|
+
const buckets = [];
|
|
12025
|
+
for (const row of rows) {
|
|
12026
|
+
let node = root;
|
|
12027
|
+
for (const item of set.items) {
|
|
12028
|
+
const value = groupingItemValue(item, row);
|
|
12029
|
+
let child = node.children.get(value);
|
|
12030
|
+
if (!child) {
|
|
12031
|
+
child = { children: /* @__PURE__ */ new Map() };
|
|
12032
|
+
node.children.set(value, child);
|
|
12033
|
+
}
|
|
12034
|
+
node = child;
|
|
12035
|
+
}
|
|
12036
|
+
if (!node.rows) {
|
|
12037
|
+
countBucket();
|
|
12038
|
+
node.rows = [];
|
|
12039
|
+
buckets.push(node.rows);
|
|
12040
|
+
}
|
|
12041
|
+
node.rows.push(row);
|
|
12042
|
+
}
|
|
12043
|
+
if (rows.length === 0 && set.items.length === 0) {
|
|
12044
|
+
countBucket();
|
|
12045
|
+
root.rows = [];
|
|
12046
|
+
buckets.push(root.rows);
|
|
12047
|
+
}
|
|
12048
|
+
const includedCanonicalIds = new Set(set.items.map((item) => item.canonicalId));
|
|
12049
|
+
for (const groupRows of buckets) {
|
|
12050
|
+
const outRow = { ...groupRows[0] };
|
|
12051
|
+
const includedValues = /* @__PURE__ */ new Map();
|
|
12052
|
+
for (const item of set.items) {
|
|
12053
|
+
if (!includedValues.has(item.canonicalId)) {
|
|
12054
|
+
includedValues.set(item.canonicalId, groupingItemValue(item, groupRows[0]));
|
|
12055
|
+
}
|
|
12056
|
+
}
|
|
12057
|
+
for (const item of spec.allItems) {
|
|
12058
|
+
const value = includedValues.get(item.canonicalId) ?? "";
|
|
12059
|
+
outRow[item.directKey] = value;
|
|
12060
|
+
if (item.unqualifiedBridgeKey !== null) {
|
|
12061
|
+
outRow[item.unqualifiedBridgeKey] = value;
|
|
12062
|
+
}
|
|
12063
|
+
}
|
|
12064
|
+
materializeAggregateColumns(outRow, groupRows, columns, resolveAggSortKind);
|
|
12065
|
+
attachGroupingRowMeta(outRow, includedCanonicalIds);
|
|
12066
|
+
result.push(outRow);
|
|
12067
|
+
}
|
|
12256
12068
|
}
|
|
12069
|
+
return result;
|
|
12257
12070
|
}
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
return [
|
|
12262
|
-
"$id",
|
|
12263
|
-
.../* @__PURE__ */ new Set([
|
|
12264
|
-
...fieldIndex.topLevel.map((field) => field.code).filter((code) => code !== "$id" && code !== "$revision"),
|
|
12265
|
-
...fieldIndex.subtables.keys()
|
|
12266
|
-
])
|
|
12267
|
-
];
|
|
12071
|
+
function groupingItemValue(item, row) {
|
|
12072
|
+
if (!row) return "";
|
|
12073
|
+
return row[item.directKey] ?? (item.unqualifiedBridgeKey === null ? void 0 : row[item.unqualifiedBridgeKey]) ?? "";
|
|
12268
12074
|
}
|
|
12269
|
-
function
|
|
12270
|
-
const
|
|
12271
|
-
|
|
12272
|
-
|
|
12075
|
+
function materializeAggregateColumns(outRow, groupRows, columns, resolveAggSortKind) {
|
|
12076
|
+
for (const col of columns) {
|
|
12077
|
+
if (col.type === "AGGREGATE") {
|
|
12078
|
+
const syntheticKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
|
|
12079
|
+
const value = String(evalAggregate(col.func, col.distinct, col.arg, col.separator, groupRows, resolveAggSortKind));
|
|
12080
|
+
outRow[col.alias ?? syntheticKey] = value;
|
|
12081
|
+
if (col.alias) outRow[syntheticKey] = value;
|
|
12082
|
+
} else if (col.type === "ARITH_AGG_COL") {
|
|
12083
|
+
const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
|
|
12084
|
+
outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows, resolveAggSortKind));
|
|
12085
|
+
} else if (col.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(col.expr)) {
|
|
12086
|
+
const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
|
|
12087
|
+
const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
|
|
12088
|
+
outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
|
|
12089
|
+
} else if (col.type === "SCALAR_VALUE_COL" && scalarValueHasAggregate2(col.expr)) {
|
|
12090
|
+
const outputKey = col.alias ?? scalarValueDefaultKey(col.expr);
|
|
12091
|
+
const resolvedExpr = resolveAggInScalarValue(col.expr, groupRows, resolveAggSortKind);
|
|
12092
|
+
outRow[outputKey] = String(evalScalarValueExpr(resolvedExpr, outRow));
|
|
12093
|
+
}
|
|
12273
12094
|
}
|
|
12274
|
-
return postImage;
|
|
12275
12095
|
}
|
|
12276
|
-
function
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12287
|
-
|
|
12096
|
+
function evalGroupByKey(key, row, resolution, columns, aliasEvaluationContext) {
|
|
12097
|
+
if (key.type === "FIELD_NAME") {
|
|
12098
|
+
if (!resolution) return row[key.name] ?? "";
|
|
12099
|
+
if (resolution.kind === "PHYSICAL") return row[resolution.runtimeKey] ?? "";
|
|
12100
|
+
if (resolution.kind === "ALIAS_SAFE") {
|
|
12101
|
+
const column = columns[resolution.columnIndex];
|
|
12102
|
+
if (!column) {
|
|
12103
|
+
throw new Error(
|
|
12104
|
+
`InternalError: GROUP BY alias column index ${resolution.columnIndex} is out of range.`
|
|
12105
|
+
);
|
|
12106
|
+
}
|
|
12107
|
+
const value = evaluateSelectColumnValue(
|
|
12108
|
+
column,
|
|
12109
|
+
row,
|
|
12110
|
+
resolution.columnIndex,
|
|
12111
|
+
aliasEvaluationContext
|
|
12112
|
+
);
|
|
12113
|
+
if (typeof value !== "string") {
|
|
12114
|
+
throw new Error("InternalError: GROUP BY alias resolved to an expanded SELECT column.");
|
|
12115
|
+
}
|
|
12116
|
+
return value;
|
|
12288
12117
|
}
|
|
12289
|
-
|
|
12118
|
+
throw new Error(
|
|
12119
|
+
`InternalError: unresolved plain GROUP BY item ${resolution.kind} reached evaluation.`
|
|
12120
|
+
);
|
|
12290
12121
|
}
|
|
12291
|
-
return
|
|
12292
|
-
|
|
12293
|
-
invalidRows: invalidRowNumbers.size,
|
|
12294
|
-
invalidRowNumbers,
|
|
12295
|
-
writeRecord
|
|
12296
|
-
};
|
|
12297
|
-
}
|
|
12298
|
-
function normalizePlainError(row, childCell = false) {
|
|
12299
|
-
return {
|
|
12300
|
-
...row,
|
|
12301
|
-
$err_value: childCell ? row["$err_value"] ?? "" : "",
|
|
12302
|
-
$err_subtable: childCell ? row["$err_subtable"] ?? "" : "",
|
|
12303
|
-
$err_subrow: childCell ? row["$err_subrow"] ?? "" : "",
|
|
12304
|
-
$err_subrow_id: childCell ? row["$err_subrow_id"] ?? "" : ""
|
|
12305
|
-
};
|
|
12122
|
+
if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
|
|
12123
|
+
return String(evalArithExpr(key.expr, row));
|
|
12306
12124
|
}
|
|
12307
|
-
function
|
|
12308
|
-
if (
|
|
12309
|
-
|
|
12310
|
-
const existing = seen.get(object);
|
|
12311
|
-
if (existing !== void 0) return existing;
|
|
12312
|
-
if (Array.isArray(value)) {
|
|
12313
|
-
const clone2 = [];
|
|
12314
|
-
seen.set(object, clone2);
|
|
12315
|
-
for (const item of value) clone2.push(deepClone(item, seen));
|
|
12316
|
-
return clone2;
|
|
12125
|
+
function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind) {
|
|
12126
|
+
if (arg.type === "WILDCARD") {
|
|
12127
|
+
return func === "COUNT" ? rows.length : 0;
|
|
12317
12128
|
}
|
|
12318
|
-
const
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12364
|
-
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
]
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12129
|
+
const strValues = [];
|
|
12130
|
+
for (const row of rows) {
|
|
12131
|
+
let strVal;
|
|
12132
|
+
if (arg.type === "FIELD_REF") {
|
|
12133
|
+
const raw = row[arg.field];
|
|
12134
|
+
if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
|
|
12135
|
+
strVal = raw;
|
|
12136
|
+
} else if (arg.type === "ARITH" || arg.type === "NUMBER" || arg.type === "STRING_FUNC") {
|
|
12137
|
+
const n = evalArithExpr(arg, row);
|
|
12138
|
+
if (isNaN(n)) continue;
|
|
12139
|
+
strVal = String(n);
|
|
12140
|
+
} else {
|
|
12141
|
+
const value = evalScalarValueExprNullable(arg, row);
|
|
12142
|
+
if (value === null) continue;
|
|
12143
|
+
if (value === "" && func !== "MIN" && func !== "MAX") continue;
|
|
12144
|
+
if (typeof value === "number" && Number.isNaN(value)) continue;
|
|
12145
|
+
strVal = String(value);
|
|
12146
|
+
}
|
|
12147
|
+
strValues.push(strVal);
|
|
12148
|
+
}
|
|
12149
|
+
const statistical = func === "STDDEV_POP" || func === "STDDEV_SAMP" || func === "VAR_POP" || func === "VAR_SAMP" || func === "MEDIAN";
|
|
12150
|
+
const numericValues = statistical ? strValues.map((value) => {
|
|
12151
|
+
const numeric = Number(value);
|
|
12152
|
+
if (!Number.isFinite(numeric)) {
|
|
12153
|
+
throw new Error(`ArgumentError: ${func} \u306E\u5F15\u6570\u306B\u975E\u6570\u5024\u307E\u305F\u306F\u975E\u6709\u9650\u306E\u5024\u304C\u3042\u308A\u307E\u3059: ${value}`);
|
|
12154
|
+
}
|
|
12155
|
+
return numeric;
|
|
12156
|
+
}) : null;
|
|
12157
|
+
const eff = distinct ? statistical ? [...new Set(numericValues)] : [...new Set(strValues)] : statistical ? numericValues : strValues;
|
|
12158
|
+
if (func === "COUNT") return eff.length;
|
|
12159
|
+
if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
|
|
12160
|
+
const comparison = func === "MIN" || func === "MAX" || func === "MODE" ? resolveAggregateArgSemantics(arg, resolveAggSortKind) : void 0;
|
|
12161
|
+
const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" || arg.type === "FIELD" || arg.type === "STRING" || arg.type === "CONCAT_OP" || arg.type === "CASE_WHEN" ? syntheticSemantics("string") : syntheticSemantics("number"));
|
|
12162
|
+
if (func === "MODE") {
|
|
12163
|
+
if (strValues.length === 0) return "";
|
|
12164
|
+
const frequencies = /* @__PURE__ */ new Map();
|
|
12165
|
+
for (const value of strValues) frequencies.set(value, (frequencies.get(value) ?? 0) + 1);
|
|
12166
|
+
let result = "";
|
|
12167
|
+
let maxFrequency = 0;
|
|
12168
|
+
for (const [candidate, frequency] of frequencies) {
|
|
12169
|
+
if (frequency > maxFrequency) {
|
|
12170
|
+
result = candidate;
|
|
12171
|
+
maxFrequency = frequency;
|
|
12172
|
+
continue;
|
|
12173
|
+
}
|
|
12174
|
+
if (frequency !== maxFrequency) continue;
|
|
12175
|
+
const canonical = compareCanonicalValues(candidate, result, semantics);
|
|
12176
|
+
if (canonical < 0 || canonical === 0 && compareCodePointStrings(candidate, result) < 0) {
|
|
12177
|
+
result = candidate;
|
|
12178
|
+
}
|
|
12179
|
+
}
|
|
12180
|
+
return result;
|
|
12181
|
+
}
|
|
12182
|
+
if (func === "MIN" || func === "MAX") {
|
|
12183
|
+
const comparableValues = eff;
|
|
12184
|
+
if (comparableValues.length === 0) return 0;
|
|
12185
|
+
let result = comparableValues[0];
|
|
12186
|
+
for (const candidate of comparableValues.slice(1)) {
|
|
12187
|
+
const cmp = compareCanonicalValues(candidate, result, semantics);
|
|
12188
|
+
if (func === "MAX" && cmp > 0 || func === "MIN" && cmp < 0) result = candidate;
|
|
12189
|
+
}
|
|
12190
|
+
return result;
|
|
12191
|
+
}
|
|
12192
|
+
const nums = statistical ? eff : eff.map(Number);
|
|
12193
|
+
switch (func) {
|
|
12194
|
+
case "SUM":
|
|
12195
|
+
return nums.reduce((a, b) => a + b, 0);
|
|
12196
|
+
case "AVG":
|
|
12197
|
+
return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
12198
|
+
case "MEDIAN": {
|
|
12199
|
+
if (nums.length === 0) return "";
|
|
12200
|
+
const sorted = [...nums].sort((a, b) => a - b);
|
|
12201
|
+
const middle = Math.floor(sorted.length / 2);
|
|
12202
|
+
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
12203
|
+
}
|
|
12204
|
+
case "VAR_POP":
|
|
12205
|
+
case "VAR_SAMP":
|
|
12206
|
+
case "STDDEV_POP":
|
|
12207
|
+
case "STDDEV_SAMP": {
|
|
12208
|
+
const sample = func === "VAR_SAMP" || func === "STDDEV_SAMP";
|
|
12209
|
+
if (nums.length === 0 || sample && nums.length < 2) return "";
|
|
12210
|
+
const origin = nums[0];
|
|
12211
|
+
let count = 0;
|
|
12212
|
+
let mean = 0;
|
|
12213
|
+
let m2 = 0;
|
|
12214
|
+
for (const rawValue of nums) {
|
|
12215
|
+
const value = rawValue - origin;
|
|
12216
|
+
count++;
|
|
12217
|
+
const delta = value - mean;
|
|
12218
|
+
mean += delta / count;
|
|
12219
|
+
m2 += delta * (value - mean);
|
|
12220
|
+
}
|
|
12221
|
+
const variance = Math.max(0, m2 / (sample ? count - 1 : count));
|
|
12222
|
+
return func === "STDDEV_POP" || func === "STDDEV_SAMP" ? Math.sqrt(variance) : variance;
|
|
12223
|
+
}
|
|
12224
|
+
}
|
|
12225
|
+
}
|
|
12226
|
+
function toAggregateFieldRef(field) {
|
|
12227
|
+
const dot = field.indexOf(".");
|
|
12228
|
+
return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
|
|
12229
|
+
}
|
|
12230
|
+
function evalAggArithExpr(node, rows, resolveAggSortKind) {
|
|
12231
|
+
if (node.type === "NUMBER") return node.value;
|
|
12232
|
+
if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
|
|
12233
|
+
const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
|
|
12234
|
+
const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
|
|
12235
|
+
switch (node.op) {
|
|
12236
|
+
case "+":
|
|
12237
|
+
return l + r;
|
|
12238
|
+
case "-":
|
|
12239
|
+
return l - r;
|
|
12240
|
+
case "*":
|
|
12241
|
+
return l * r;
|
|
12242
|
+
case "/":
|
|
12243
|
+
return r !== 0 ? l / r : NaN;
|
|
12244
|
+
case "%":
|
|
12245
|
+
return r !== 0 ? l % r : NaN;
|
|
12246
|
+
}
|
|
12247
|
+
}
|
|
12248
|
+
function aggArithDefaultKey(node) {
|
|
12249
|
+
return aggregateOperandLabel(node);
|
|
12250
|
+
}
|
|
12251
|
+
function resolveAggregateArgSemantics(arg, resolver) {
|
|
12252
|
+
if (arg.type === "FIELD_REF") return resolver?.(toAggregateFieldRef(arg.field)) ?? "string";
|
|
12253
|
+
if (arg.type === "FIELD") return resolver?.(arg) ?? "string";
|
|
12254
|
+
if (arg.type === "NUMBER" || arg.type === "ARITH" || arg.type === "SCALAR_ARITH") return "number";
|
|
12255
|
+
if (arg.type === "STRING" || arg.type === "CONCAT_OP" || arg.type === "VARIABLE") return "string";
|
|
12256
|
+
if (arg.type === "STRING_FUNC") {
|
|
12257
|
+
const numeric = /* @__PURE__ */ new Set(["LENGTH", "LENGTH_CHAR", "INSTR", "ROUND", "FLOOR", "CEIL", "TRUNCATE", "YEAR", "MONTH", "DAY", "DATEDIFF", "ABS", "MOD", "POWER", "SQRT", "DAYOFWEEK", "QUARTER", "WEEK"]);
|
|
12258
|
+
if (arg.func === "CAST") return arg.args[1]?.type === "STRING" && arg.args[1].value === "NUMBER" ? "number" : "string";
|
|
12259
|
+
return numeric.has(arg.func) ? "number" : "string";
|
|
12260
|
+
}
|
|
12261
|
+
const results = [...arg.branches.map((branch) => branch.result), ...arg.elseResult === null ? [] : [arg.elseResult]].filter((result) => result.type !== "ARRAY").map((result) => resolveAggregateArgSemantics(result, resolver));
|
|
12262
|
+
if (results.length === 0 || results.some((result) => result === void 0)) return "string";
|
|
12263
|
+
const kinds = results.map((result) => typeof result === "string" ? result : result.compareMode === "number" || result.compareMode === "recordNumber" ? "number" : "string");
|
|
12264
|
+
if (!kinds.every((kind) => kind === kinds[0])) return "string";
|
|
12265
|
+
const first = results[0];
|
|
12266
|
+
return results.every((result) => JSON.stringify(result) === JSON.stringify(first)) ? first : kinds[0];
|
|
12267
|
+
}
|
|
12268
|
+
function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
|
|
12269
|
+
if (having === null) return rows;
|
|
12270
|
+
return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
|
|
12271
|
+
}
|
|
12272
|
+
function applyDistinct(rows, columns, scalarCache, resolveFieldType, resolveFieldSemantics2) {
|
|
12273
|
+
if (rows.length === 0) return rows;
|
|
12274
|
+
const keyFor = buildDistinctKeyBuilder(rows, columns, {
|
|
12275
|
+
scalarCache,
|
|
12276
|
+
resolveFieldType,
|
|
12277
|
+
resolveFieldSemantics: resolveFieldSemantics2
|
|
12278
|
+
});
|
|
12279
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12280
|
+
return rows.filter((row) => {
|
|
12281
|
+
const key = keyFor(row);
|
|
12282
|
+
if (seen.has(key)) return false;
|
|
12283
|
+
seen.add(key);
|
|
12284
|
+
return true;
|
|
12285
|
+
});
|
|
12286
|
+
}
|
|
12287
|
+
function buildDistinctKeyBuilder(rows, columns, context) {
|
|
12288
|
+
let sortedWildcardKeys = [];
|
|
12289
|
+
if (columns.some((c) => c.type === "WILDCARD")) {
|
|
12290
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
12291
|
+
for (const row of rows) {
|
|
12292
|
+
for (const k of Object.keys(row)) allKeys.add(k);
|
|
12293
|
+
}
|
|
12294
|
+
sortedWildcardKeys = [...allKeys].sort();
|
|
12295
|
+
}
|
|
12296
|
+
let sortedParentKeys = [];
|
|
12297
|
+
if (columns.some((c) => c.type === "PARENT_WILDCARD")) {
|
|
12298
|
+
const parentKeys = /* @__PURE__ */ new Set();
|
|
12299
|
+
for (const row of rows) {
|
|
12300
|
+
for (const k of Object.keys(row)) {
|
|
12301
|
+
if (k.startsWith("_p.")) parentKeys.add(k);
|
|
12302
|
+
}
|
|
12303
|
+
}
|
|
12304
|
+
sortedParentKeys = [...parentKeys].sort();
|
|
12305
|
+
}
|
|
12306
|
+
const distinctContext = {
|
|
12307
|
+
...context,
|
|
12308
|
+
wildcardKeys: sortedWildcardKeys,
|
|
12309
|
+
parentWildcardKeys: sortedParentKeys
|
|
12310
|
+
};
|
|
12311
|
+
return (row) => JSON.stringify(buildDistinctTuple(columns, row, distinctContext));
|
|
12312
|
+
}
|
|
12313
|
+
function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2, aliasEvaluator) {
|
|
12314
|
+
if (orderBy.length === 0) return rows;
|
|
12315
|
+
return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2, aliasEvaluator).rows.map((item) => item.row);
|
|
12316
|
+
}
|
|
12317
|
+
function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2, aliasEvaluator) {
|
|
12318
|
+
const keyMeta = orderBy.map(({ key }) => {
|
|
12319
|
+
if (key.type === "ARITH_KEY") return { semantics: syntheticSemantics("number") };
|
|
12320
|
+
if (key.type === "FUNC_KEY") {
|
|
12321
|
+
return { semantics: syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(key.expr.func) ? "number" : "string") };
|
|
12322
|
+
}
|
|
12323
|
+
if (key.type === "GROUPING_KEY") return { semantics: syntheticSemantics("number") };
|
|
12324
|
+
const semantics = fieldSemantics2?.get(key.name);
|
|
12325
|
+
if (semantics) return { semantics };
|
|
12326
|
+
const orderMap = optionOrders?.get(key.name);
|
|
12327
|
+
if (orderMap) {
|
|
12328
|
+
return {
|
|
12329
|
+
semantics: {
|
|
12330
|
+
fieldType: "MULTI_SELECT",
|
|
12331
|
+
compareMode: "option",
|
|
12332
|
+
inSubtable: false,
|
|
12333
|
+
requiresCollectionOperators: false,
|
|
12334
|
+
optionOrder: orderMap
|
|
12335
|
+
}
|
|
12336
|
+
};
|
|
12337
|
+
}
|
|
12338
|
+
return { semantics: syntheticSemantics(sortKinds?.get(key.name) ?? "string") };
|
|
12339
|
+
});
|
|
12340
|
+
const decorated = rows.map((row) => ({
|
|
12341
|
+
row,
|
|
12342
|
+
keys: orderBy.map(({ key }, i) => {
|
|
12343
|
+
const s = evalOrderKey(key, row, aliasEvaluator);
|
|
12344
|
+
return { s };
|
|
12345
|
+
})
|
|
12346
|
+
}));
|
|
12347
|
+
const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
|
|
12348
|
+
decorated.sort(compare);
|
|
12349
|
+
return { rows: decorated, compare };
|
|
12350
|
+
}
|
|
12351
|
+
function compareDecoratedRows(a, b, orderBy, keyMeta) {
|
|
12352
|
+
for (let i = 0; i < orderBy.length; i++) {
|
|
12353
|
+
const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
|
|
12354
|
+
if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
|
|
12355
|
+
}
|
|
12356
|
+
return 0;
|
|
12357
|
+
}
|
|
12358
|
+
function compareSortKeys(a, b, meta) {
|
|
12359
|
+
return compareCanonicalValues(a.s, b.s, meta.semantics);
|
|
12360
|
+
}
|
|
12361
|
+
var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
12362
|
+
"LENGTH",
|
|
12363
|
+
"LENGTH_CHAR",
|
|
12364
|
+
"INSTR",
|
|
12365
|
+
"ROUND",
|
|
12366
|
+
"FLOOR",
|
|
12367
|
+
"CEIL",
|
|
12368
|
+
"TRUNCATE",
|
|
12369
|
+
"YEAR",
|
|
12370
|
+
"MONTH",
|
|
12371
|
+
"DAY",
|
|
12372
|
+
"DATEDIFF",
|
|
12373
|
+
"ABS",
|
|
12374
|
+
"MOD",
|
|
12375
|
+
"POWER",
|
|
12376
|
+
"SQRT",
|
|
12377
|
+
"DAYOFWEEK",
|
|
12378
|
+
"QUARTER",
|
|
12379
|
+
"WEEK"
|
|
12409
12380
|
]);
|
|
12410
|
-
function
|
|
12411
|
-
|
|
12381
|
+
function evalOrderKey(key, row, aliasEvaluator) {
|
|
12382
|
+
switch (key.type) {
|
|
12383
|
+
case "FIELD_NAME":
|
|
12384
|
+
return aliasEvaluator?.(key.name, row) ?? row[key.name] ?? "";
|
|
12385
|
+
case "ARITH_KEY":
|
|
12386
|
+
return String(evalArithExpr(key.expr, row));
|
|
12387
|
+
case "FUNC_KEY":
|
|
12388
|
+
return evalStringFunc(key.expr, row);
|
|
12389
|
+
case "GROUPING_KEY":
|
|
12390
|
+
return evalGroupingRef(key.ref, row);
|
|
12391
|
+
}
|
|
12392
|
+
}
|
|
12393
|
+
function buildOrderByAliasEvaluator(columns, scalarCache, resolveFieldType, resolveFieldSemantics2) {
|
|
12394
|
+
const evaluators = /* @__PURE__ */ new Map();
|
|
12395
|
+
for (const [columnIndex, column] of columns.entries()) {
|
|
12396
|
+
if (!("alias" in column) || column.alias === null) continue;
|
|
12397
|
+
const alias = column.alias;
|
|
12398
|
+
switch (column.type) {
|
|
12399
|
+
case "FIELD":
|
|
12400
|
+
evaluators.set(alias, (row) => resolveFieldRef(row, column.field));
|
|
12401
|
+
break;
|
|
12402
|
+
case "LITERAL_COL":
|
|
12403
|
+
evaluators.set(alias, () => column.value);
|
|
12404
|
+
break;
|
|
12405
|
+
case "AGGREGATE": {
|
|
12406
|
+
const source = aggregateSyntheticName(column.func, column.distinct, column.arg);
|
|
12407
|
+
evaluators.set(alias, (row) => row[alias] ?? row[source] ?? "0");
|
|
12408
|
+
break;
|
|
12409
|
+
}
|
|
12410
|
+
case "ARITH_AGG_COL": {
|
|
12411
|
+
const source = aggArithDefaultKey(column.expr);
|
|
12412
|
+
evaluators.set(alias, (row) => row[alias] ?? row[source] ?? "0");
|
|
12413
|
+
break;
|
|
12414
|
+
}
|
|
12415
|
+
case "WINDOW_COL":
|
|
12416
|
+
evaluators.set(alias, (row) => row[alias] ?? "");
|
|
12417
|
+
break;
|
|
12418
|
+
case "ARITH_COL":
|
|
12419
|
+
evaluators.set(alias, (row) => String(evalArithExpr(column.expr, row)));
|
|
12420
|
+
break;
|
|
12421
|
+
case "STRFUNC_COL": {
|
|
12422
|
+
const source = stringFuncDefaultKey(column.expr);
|
|
12423
|
+
evaluators.set(alias, (row) => hasAggregateInStringFuncExpr2(column.expr) ? row[alias] ?? row[source] ?? evalStringFunc(column.expr, row, resolveFieldType, resolveFieldSemantics2) : evalStringFunc(column.expr, row, resolveFieldType, resolveFieldSemantics2));
|
|
12424
|
+
break;
|
|
12425
|
+
}
|
|
12426
|
+
case "CASE_COL":
|
|
12427
|
+
evaluators.set(alias, (row) => evalCaseWhen(column.expr, row, resolveFieldType, resolveFieldSemantics2));
|
|
12428
|
+
break;
|
|
12429
|
+
case "SCALAR_VALUE_COL": {
|
|
12430
|
+
const source = scalarValueDefaultKey(column.expr);
|
|
12431
|
+
evaluators.set(alias, (row) => scalarValueHasAggregate2(column.expr) ? row[alias] ?? row[source] ?? "" : String(evalScalarValueExpr(column.expr, row, resolveFieldType, resolveFieldSemantics2)));
|
|
12432
|
+
break;
|
|
12433
|
+
}
|
|
12434
|
+
case "SCALAR_SUBQUERY_COL":
|
|
12435
|
+
evaluators.set(alias, () => scalarCache?.get(columnIndex) ?? "");
|
|
12436
|
+
break;
|
|
12437
|
+
case "GROUPING_COL":
|
|
12438
|
+
evaluators.set(alias, (row) => evalGroupingRef(column.ref, row));
|
|
12439
|
+
break;
|
|
12440
|
+
case "VARIABLE_COL":
|
|
12441
|
+
break;
|
|
12442
|
+
}
|
|
12443
|
+
}
|
|
12444
|
+
return (name, row) => evaluators.get(name)?.(row);
|
|
12412
12445
|
}
|
|
12413
|
-
function
|
|
12414
|
-
|
|
12415
|
-
|
|
12446
|
+
function applyWindow(rows, columns, optionOrders, sortKinds, fieldSemantics2) {
|
|
12447
|
+
const windows = columns.filter((column) => column.type === "WINDOW_COL");
|
|
12448
|
+
if (rows.length === 0 || windows.length === 0) return rows;
|
|
12449
|
+
for (const window of windows) {
|
|
12450
|
+
const partitions = /* @__PURE__ */ new Map();
|
|
12451
|
+
for (const row of rows) {
|
|
12452
|
+
const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
|
|
12453
|
+
const partition = partitions.get(key);
|
|
12454
|
+
if (partition) partition.push(row);
|
|
12455
|
+
else partitions.set(key, [row]);
|
|
12456
|
+
}
|
|
12457
|
+
for (const partition of partitions.values()) {
|
|
12458
|
+
const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
|
|
12459
|
+
const sorted = sortedResult.rows;
|
|
12460
|
+
let rank = 1;
|
|
12461
|
+
let denseRank = 1;
|
|
12462
|
+
for (let index = 0; index < sorted.length; index++) {
|
|
12463
|
+
if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
|
|
12464
|
+
rank = index + 1;
|
|
12465
|
+
denseRank++;
|
|
12466
|
+
}
|
|
12467
|
+
const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
|
|
12468
|
+
sorted[index].row[window.alias] = String(value);
|
|
12469
|
+
}
|
|
12470
|
+
}
|
|
12416
12471
|
}
|
|
12417
|
-
return
|
|
12472
|
+
return rows;
|
|
12418
12473
|
}
|
|
12419
|
-
function
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
case "
|
|
12431
|
-
|
|
12432
|
-
case "
|
|
12433
|
-
const
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12474
|
+
function resolveWindowField(row, ref) {
|
|
12475
|
+
const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
|
|
12476
|
+
return resolveFieldRef(row, name);
|
|
12477
|
+
}
|
|
12478
|
+
function applyLimit(rows, limit, offset) {
|
|
12479
|
+
const start = offset ?? 0;
|
|
12480
|
+
if (limit === null) return rows.slice(start);
|
|
12481
|
+
return rows.slice(start, start + limit);
|
|
12482
|
+
}
|
|
12483
|
+
function evaluateSelectColumnValue(column, row, columnIndex, context = {}) {
|
|
12484
|
+
switch (column.type) {
|
|
12485
|
+
case "VARIABLE_COL":
|
|
12486
|
+
throw new Error(`internal error: unresolved SELECT variable @${column.name}`);
|
|
12487
|
+
case "WILDCARD": {
|
|
12488
|
+
const keys = context.wildcardKeys ?? Object.keys(row);
|
|
12489
|
+
return {
|
|
12490
|
+
kind: "EXPANDED",
|
|
12491
|
+
entries: keys.map((key) => [key, row[key] !== void 0 ? row[key] : null])
|
|
12492
|
+
};
|
|
12493
|
+
}
|
|
12494
|
+
case "PARENT_WILDCARD": {
|
|
12495
|
+
const keys = context.parentWildcardKeys ?? Object.keys(row).filter((key) => key.startsWith("_p.")).sort();
|
|
12496
|
+
return {
|
|
12497
|
+
kind: "EXPANDED",
|
|
12498
|
+
entries: keys.map((key) => [key, row[key] !== void 0 ? row[key] : null])
|
|
12499
|
+
};
|
|
12500
|
+
}
|
|
12501
|
+
case "FIELD":
|
|
12502
|
+
return resolveFieldRef(row, column.field);
|
|
12503
|
+
case "LITERAL_COL":
|
|
12504
|
+
return column.value;
|
|
12505
|
+
case "AGGREGATE": {
|
|
12506
|
+
const source = aggregateSyntheticName(column.func, column.distinct, column.arg);
|
|
12507
|
+
return row[column.alias ?? source] ?? row[source] ?? "0";
|
|
12508
|
+
}
|
|
12509
|
+
case "ARITH_AGG_COL": {
|
|
12510
|
+
const source = column.alias ?? aggArithDefaultKey(column.expr);
|
|
12511
|
+
return row[source] ?? "0";
|
|
12512
|
+
}
|
|
12513
|
+
case "ARITH_COL":
|
|
12514
|
+
return String(evalArithExpr(column.expr, row));
|
|
12515
|
+
case "CASE_COL":
|
|
12516
|
+
return evalCaseWhen(
|
|
12517
|
+
column.expr,
|
|
12518
|
+
row,
|
|
12519
|
+
context.resolveFieldType,
|
|
12520
|
+
context.resolveFieldSemantics
|
|
12521
|
+
);
|
|
12522
|
+
case "GROUPING_COL":
|
|
12523
|
+
return evalGroupingRef(column.ref, row);
|
|
12524
|
+
case "STRFUNC_COL": {
|
|
12525
|
+
const source = stringFuncDefaultKey(column.expr);
|
|
12526
|
+
return hasAggregateInStringFuncExpr2(column.expr) ? row[column.alias ?? source] ?? row[source] ?? evalStringFunc(
|
|
12527
|
+
column.expr,
|
|
12528
|
+
row,
|
|
12529
|
+
context.resolveFieldType,
|
|
12530
|
+
context.resolveFieldSemantics
|
|
12531
|
+
) : evalStringFunc(
|
|
12532
|
+
column.expr,
|
|
12533
|
+
row,
|
|
12534
|
+
context.resolveFieldType,
|
|
12535
|
+
context.resolveFieldSemantics
|
|
12536
|
+
);
|
|
12537
|
+
}
|
|
12538
|
+
case "SCALAR_VALUE_COL": {
|
|
12539
|
+
const source = scalarValueDefaultKey(column.expr);
|
|
12540
|
+
return scalarValueHasAggregate2(column.expr) ? row[column.alias ?? source] ?? row[source] ?? "" : String(evalScalarValueExpr(
|
|
12541
|
+
column.expr,
|
|
12542
|
+
row,
|
|
12543
|
+
context.resolveFieldType,
|
|
12544
|
+
context.resolveFieldSemantics
|
|
12545
|
+
));
|
|
12546
|
+
}
|
|
12547
|
+
case "SCALAR_SUBQUERY_COL":
|
|
12548
|
+
return context.scalarCache?.get(columnIndex) ?? "";
|
|
12549
|
+
case "WINDOW_COL":
|
|
12550
|
+
return row[column.alias] ?? "";
|
|
12551
|
+
}
|
|
12552
|
+
}
|
|
12553
|
+
function buildDistinctTuple(columns, row, context = {}) {
|
|
12554
|
+
return columns.map((column, columnIndex) => {
|
|
12555
|
+
const value = evaluateSelectColumnValue(column, row, columnIndex, context);
|
|
12556
|
+
return typeof value === "string" ? value : value.entries.map(([, entryValue]) => entryValue);
|
|
12557
|
+
});
|
|
12558
|
+
}
|
|
12559
|
+
function project(rows, columns, scalarCache, resolveFieldType, sourceColumns2, resolveFieldSemantics2, hiddenQualifiedAliases) {
|
|
12560
|
+
if (columns.length === 1 && columns[0].type === "WILDCARD") {
|
|
12561
|
+
const projected2 = rows.map((row) => {
|
|
12562
|
+
const visible = stripHiddenQualifiedColumns(
|
|
12563
|
+
stripParentShortcutColumns(row),
|
|
12564
|
+
hiddenQualifiedAliases
|
|
12565
|
+
);
|
|
12566
|
+
const value = evaluateSelectColumnValue(columns[0], row, 0, {
|
|
12567
|
+
wildcardKeys: Object.keys(visible)
|
|
12568
|
+
});
|
|
12569
|
+
const out = {};
|
|
12570
|
+
if (typeof value !== "string") {
|
|
12571
|
+
for (const [key, entryValue] of value.entries) {
|
|
12572
|
+
if (entryValue !== null) out[key] = entryValue;
|
|
12573
|
+
}
|
|
12574
|
+
}
|
|
12575
|
+
return out;
|
|
12576
|
+
});
|
|
12577
|
+
const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns2 ?? []];
|
|
12578
|
+
return { rows: projected2, columns: cols };
|
|
12579
|
+
}
|
|
12580
|
+
const defaultFieldKeys = buildDefaultFieldOutputKeys(columns);
|
|
12581
|
+
const hasWildcard = columns.some(
|
|
12582
|
+
(col) => col.type === "WILDCARD" || col.type === "PARENT_WILDCARD"
|
|
12583
|
+
);
|
|
12584
|
+
const outputKeys = hasWildcard ? null : computeOutputKeys(columns, defaultFieldKeys);
|
|
12585
|
+
const orderedKeys = outputKeys ?? [];
|
|
12586
|
+
if (hasWildcard && rows.length === 0) {
|
|
12587
|
+
return { rows: [], columns: computeExplicitOutputKeys(columns, defaultFieldKeys) };
|
|
12588
|
+
}
|
|
12589
|
+
const projected = rows.map((row, rowIdx) => {
|
|
12590
|
+
const out = {};
|
|
12591
|
+
const evaluationContext = {
|
|
12592
|
+
scalarCache,
|
|
12593
|
+
resolveFieldType,
|
|
12594
|
+
resolveFieldSemantics: resolveFieldSemantics2,
|
|
12595
|
+
wildcardKeys: Object.keys(stripHiddenQualifiedColumns(
|
|
12596
|
+
stripParentShortcutColumns(row),
|
|
12597
|
+
hiddenQualifiedAliases
|
|
12598
|
+
)),
|
|
12599
|
+
parentWildcardKeys: Object.keys(row).filter((key) => key.startsWith("_p.")).sort()
|
|
12600
|
+
};
|
|
12601
|
+
for (const [colIdx, col] of columns.entries()) {
|
|
12602
|
+
const value = evaluateSelectColumnValue(col, row, colIdx, evaluationContext);
|
|
12603
|
+
switch (col.type) {
|
|
12604
|
+
case "VARIABLE_COL":
|
|
12605
|
+
break;
|
|
12606
|
+
case "WILDCARD":
|
|
12607
|
+
if (typeof value !== "string") {
|
|
12608
|
+
for (const [key, entryValue] of value.entries) {
|
|
12609
|
+
if (entryValue !== null) out[key] = entryValue;
|
|
12610
|
+
}
|
|
12611
|
+
}
|
|
12612
|
+
break;
|
|
12613
|
+
case "PARENT_WILDCARD": {
|
|
12614
|
+
if (typeof value !== "string") {
|
|
12615
|
+
for (const [key, entryValue] of value.entries) {
|
|
12616
|
+
if (entryValue !== null) out[key] = entryValue;
|
|
12617
|
+
if (rowIdx === 0) orderedKeys.push(key);
|
|
12618
|
+
}
|
|
12619
|
+
}
|
|
12620
|
+
break;
|
|
12621
|
+
}
|
|
12622
|
+
case "FIELD": {
|
|
12623
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
12624
|
+
out[key] = value;
|
|
12625
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12626
|
+
break;
|
|
12627
|
+
}
|
|
12628
|
+
case "LITERAL_COL": {
|
|
12629
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? `'${col.value}'`;
|
|
12630
|
+
out[key] = value;
|
|
12631
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12632
|
+
break;
|
|
12633
|
+
}
|
|
12634
|
+
case "AGGREGATE": {
|
|
12635
|
+
const srcKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
|
|
12636
|
+
const dstKey = outputKeys?.[colIdx] ?? col.alias ?? srcKey;
|
|
12637
|
+
out[dstKey] = value;
|
|
12638
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(dstKey);
|
|
12639
|
+
break;
|
|
12640
|
+
}
|
|
12641
|
+
case "ARITH_AGG_COL": {
|
|
12642
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? aggArithDefaultKey(col.expr);
|
|
12643
|
+
out[key] = value;
|
|
12644
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12645
|
+
break;
|
|
12646
|
+
}
|
|
12647
|
+
case "ARITH_COL": {
|
|
12648
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? arithColDefaultKey(col.expr);
|
|
12649
|
+
out[key] = value;
|
|
12650
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12651
|
+
break;
|
|
12652
|
+
}
|
|
12653
|
+
case "CASE_COL": {
|
|
12654
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
|
|
12655
|
+
out[key] = value;
|
|
12656
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12657
|
+
break;
|
|
12658
|
+
}
|
|
12659
|
+
case "GROUPING_COL": {
|
|
12660
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? `GROUPING(${col.ref.field.tableAlias ? `${col.ref.field.tableAlias}.` : ""}${col.ref.field.field})`;
|
|
12661
|
+
out[key] = value;
|
|
12662
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12663
|
+
break;
|
|
12664
|
+
}
|
|
12665
|
+
case "STRFUNC_COL": {
|
|
12666
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? stringFuncDefaultKey(col.expr);
|
|
12667
|
+
out[key] = value;
|
|
12668
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12669
|
+
break;
|
|
12670
|
+
}
|
|
12671
|
+
case "SCALAR_VALUE_COL": {
|
|
12672
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? scalarValueDefaultKey(col.expr);
|
|
12673
|
+
out[key] = value;
|
|
12674
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12675
|
+
break;
|
|
12676
|
+
}
|
|
12677
|
+
case "SCALAR_SUBQUERY_COL": {
|
|
12678
|
+
const key = outputKeys?.[colIdx] ?? col.alias ?? "(subquery)";
|
|
12679
|
+
out[key] = value;
|
|
12680
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12681
|
+
break;
|
|
12682
|
+
}
|
|
12683
|
+
case "WINDOW_COL": {
|
|
12684
|
+
const key = outputKeys?.[colIdx] ?? col.alias;
|
|
12685
|
+
out[key] = value;
|
|
12686
|
+
if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
|
|
12687
|
+
break;
|
|
12688
|
+
}
|
|
12437
12689
|
}
|
|
12438
|
-
return requireExactFunctionPushdown({
|
|
12439
|
-
capability: "LOCAL_ONLY",
|
|
12440
|
-
reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }, ...inner.reasons]
|
|
12441
|
-
});
|
|
12442
|
-
}
|
|
12443
|
-
case "LOGICAL": {
|
|
12444
|
-
const left = classifyNode(where.left, resolveField2);
|
|
12445
|
-
const right = classifyNode(where.right, resolveField2);
|
|
12446
|
-
return combineLogical(where.op, left, right);
|
|
12447
12690
|
}
|
|
12448
|
-
|
|
12691
|
+
return out;
|
|
12692
|
+
});
|
|
12693
|
+
return { rows: projected, columns: orderedKeys };
|
|
12449
12694
|
}
|
|
12450
|
-
function
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
const
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
if (right.values.length === 1 && functions.length === 1) {
|
|
12463
|
-
return classifyLegacyKintoneFunctionBinary(op, left, functions[0], resolveField2);
|
|
12464
|
-
}
|
|
12465
|
-
return legacyKintoneFunctionUnsupported(
|
|
12466
|
-
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
12467
|
-
functions[0].name,
|
|
12468
|
-
left.type === "FIELD" ? left.field : void 0,
|
|
12469
|
-
left.type === "FIELD" ? resolveField2(left)?.fieldType : void 0,
|
|
12470
|
-
normalizeOperator(op)
|
|
12471
|
-
);
|
|
12695
|
+
function computeOutputKeys(columns, defaultFieldKeys) {
|
|
12696
|
+
return columns.map((col, colIdx) => computeOutputKey(col, colIdx, defaultFieldKeys));
|
|
12697
|
+
}
|
|
12698
|
+
function computeExplicitOutputKeys(columns, defaultFieldKeys) {
|
|
12699
|
+
const keys = [];
|
|
12700
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12701
|
+
for (const [colIdx, col] of columns.entries()) {
|
|
12702
|
+
if (col.type === "WILDCARD" || col.type === "PARENT_WILDCARD") continue;
|
|
12703
|
+
const key = computeOutputKey(col, colIdx, defaultFieldKeys);
|
|
12704
|
+
if (!seen.has(key)) {
|
|
12705
|
+
seen.add(key);
|
|
12706
|
+
keys.push(key);
|
|
12472
12707
|
}
|
|
12473
12708
|
}
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12709
|
+
return keys;
|
|
12710
|
+
}
|
|
12711
|
+
function computeOutputKey(col, colIdx, defaultFieldKeys) {
|
|
12712
|
+
switch (col.type) {
|
|
12713
|
+
case "VARIABLE_COL":
|
|
12714
|
+
throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
|
|
12715
|
+
case "FIELD":
|
|
12716
|
+
return col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
|
|
12717
|
+
case "LITERAL_COL":
|
|
12718
|
+
return col.alias ?? `'${col.value}'`;
|
|
12719
|
+
case "AGGREGATE":
|
|
12720
|
+
return col.alias ?? aggregateSyntheticName(col.func, col.distinct, col.arg);
|
|
12721
|
+
case "ARITH_AGG_COL":
|
|
12722
|
+
return col.alias ?? aggArithDefaultKey(col.expr);
|
|
12723
|
+
case "ARITH_COL":
|
|
12724
|
+
return col.alias ?? arithColDefaultKey(col.expr);
|
|
12725
|
+
case "CASE_COL":
|
|
12726
|
+
return col.alias ?? "case";
|
|
12727
|
+
case "STRFUNC_COL":
|
|
12728
|
+
return col.alias ?? stringFuncDefaultKey(col.expr);
|
|
12729
|
+
case "SCALAR_VALUE_COL":
|
|
12730
|
+
return col.alias ?? scalarValueDefaultKey(col.expr);
|
|
12731
|
+
case "GROUPING_COL":
|
|
12732
|
+
return col.alias ?? `GROUPING(${col.ref.field.tableAlias ? `${col.ref.field.tableAlias}.` : ""}${col.ref.field.field})`;
|
|
12733
|
+
case "SCALAR_SUBQUERY_COL":
|
|
12734
|
+
return col.alias ?? "(subquery)";
|
|
12735
|
+
case "WINDOW_COL":
|
|
12736
|
+
return col.alias;
|
|
12737
|
+
case "WILDCARD":
|
|
12738
|
+
case "PARENT_WILDCARD":
|
|
12739
|
+
throw new Error("internal: computeOutputKey received a wildcard column");
|
|
12487
12740
|
}
|
|
12488
|
-
|
|
12489
|
-
|
|
12741
|
+
}
|
|
12742
|
+
function buildDefaultFieldOutputKeys(columns) {
|
|
12743
|
+
const qualifierCollisionCount = /* @__PURE__ */ new Map();
|
|
12744
|
+
for (const col of columns) {
|
|
12745
|
+
if (col.type !== "FIELD" || col.alias) continue;
|
|
12746
|
+
const unqualified = stripTableQualifier(col.field);
|
|
12747
|
+
qualifierCollisionCount.set(unqualified, (qualifierCollisionCount.get(unqualified) ?? 0) + 1);
|
|
12490
12748
|
}
|
|
12491
|
-
const
|
|
12492
|
-
const
|
|
12493
|
-
|
|
12494
|
-
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
reasons: [{
|
|
12499
|
-
code: "WHERE_EXACT",
|
|
12500
|
-
field: left.field,
|
|
12501
|
-
fieldType: semantics.fieldType,
|
|
12502
|
-
operator: nativeOp
|
|
12503
|
-
}]
|
|
12504
|
-
};
|
|
12749
|
+
const keys = /* @__PURE__ */ new Map();
|
|
12750
|
+
for (const [idx, col] of columns.entries()) {
|
|
12751
|
+
if (col.type !== "FIELD" || col.alias) continue;
|
|
12752
|
+
const unqualified = stripTableQualifier(col.field);
|
|
12753
|
+
const duplicate = (qualifierCollisionCount.get(unqualified) ?? 0) > 1;
|
|
12754
|
+
const hasTableQualifier = col.field.includes(".") && !col.field.startsWith("_p.");
|
|
12755
|
+
keys.set(idx, duplicate && hasTableQualifier ? col.field : unqualified);
|
|
12505
12756
|
}
|
|
12506
|
-
return
|
|
12507
|
-
capability: "LOCAL_ONLY",
|
|
12508
|
-
reasons: [{
|
|
12509
|
-
code: "WHERE_RESIDUAL",
|
|
12510
|
-
field: left.field,
|
|
12511
|
-
fieldType: semantics.fieldType,
|
|
12512
|
-
operator: nativeOp
|
|
12513
|
-
}]
|
|
12514
|
-
};
|
|
12757
|
+
return keys;
|
|
12515
12758
|
}
|
|
12516
|
-
function
|
|
12517
|
-
|
|
12759
|
+
function stripTableQualifier(field) {
|
|
12760
|
+
if (field.startsWith("_p.")) return field;
|
|
12761
|
+
const dot = field.indexOf(".");
|
|
12762
|
+
if (dot <= 0) return field;
|
|
12763
|
+
const unqualified = field.slice(dot + 1);
|
|
12764
|
+
return unqualified || field;
|
|
12518
12765
|
}
|
|
12519
|
-
function
|
|
12520
|
-
const
|
|
12521
|
-
const
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
12525
|
-
functionName,
|
|
12526
|
-
void 0,
|
|
12527
|
-
void 0,
|
|
12528
|
-
operator
|
|
12529
|
-
);
|
|
12766
|
+
function stripParentShortcutColumns(row) {
|
|
12767
|
+
const out = {};
|
|
12768
|
+
for (const [k, v] of Object.entries(row)) {
|
|
12769
|
+
if (k.startsWith("_p.")) continue;
|
|
12770
|
+
out[k] = v;
|
|
12530
12771
|
}
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
-
|
|
12538
|
-
|
|
12539
|
-
|
|
12540
|
-
|
|
12772
|
+
return out;
|
|
12773
|
+
}
|
|
12774
|
+
function arithColDefaultKey(expr) {
|
|
12775
|
+
const nodeLabel = (n) => {
|
|
12776
|
+
if (n.type === "FIELD_REF") return n.field;
|
|
12777
|
+
if (n.type === "NUMBER") return numberLiteralText(n);
|
|
12778
|
+
if (n.type === "STRING_FUNC") return stringFuncDefaultKey(n);
|
|
12779
|
+
return `(${nodeLabel(n.left)}${n.op}${nodeLabel(n.right)})`;
|
|
12780
|
+
};
|
|
12781
|
+
if (expr.type === "ARITH") {
|
|
12782
|
+
return `${nodeLabel(expr.left)}${expr.op}${nodeLabel(expr.right)}`;
|
|
12541
12783
|
}
|
|
12542
|
-
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12784
|
+
return nodeLabel(expr);
|
|
12785
|
+
}
|
|
12786
|
+
function stringFuncDefaultKey(expr) {
|
|
12787
|
+
const argStrs = expr.args.map((a) => {
|
|
12788
|
+
if (a.type === "AGG_REF" || a.type === "AGG_ARITH") return aggArithDefaultKey(a);
|
|
12789
|
+
return scalarValueDefaultKey(a);
|
|
12790
|
+
});
|
|
12791
|
+
return `${expr.func}(${argStrs.join(",")})`;
|
|
12792
|
+
}
|
|
12793
|
+
function scalarValueDefaultKey(expr) {
|
|
12794
|
+
switch (expr.type) {
|
|
12795
|
+
case "STRING":
|
|
12796
|
+
return `'${expr.value}'`;
|
|
12797
|
+
case "NUMBER":
|
|
12798
|
+
return numberLiteralText(expr);
|
|
12799
|
+
case "VARIABLE":
|
|
12800
|
+
return `@${expr.name}`;
|
|
12801
|
+
case "FIELD":
|
|
12802
|
+
return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
|
|
12803
|
+
case "STRING_FUNC":
|
|
12804
|
+
return stringFuncDefaultKey(expr);
|
|
12805
|
+
case "CASE_WHEN":
|
|
12806
|
+
return "case";
|
|
12807
|
+
case "SCALAR_ARITH":
|
|
12808
|
+
return `${scalarValueDefaultKey(expr.left)}${expr.op}${scalarValueDefaultKey(expr.right)}`;
|
|
12809
|
+
case "CONCAT_OP":
|
|
12810
|
+
return `${scalarValueDefaultKey(expr.left)}||${scalarValueDefaultKey(expr.right)}`;
|
|
12551
12811
|
}
|
|
12552
|
-
return {
|
|
12553
|
-
capability: "EXACT_PUSHDOWN",
|
|
12554
|
-
reasons: [{
|
|
12555
|
-
code: "WHERE_EXACT",
|
|
12556
|
-
functionName,
|
|
12557
|
-
field: left.field,
|
|
12558
|
-
fieldType: semantics.fieldType,
|
|
12559
|
-
operator
|
|
12560
|
-
}]
|
|
12561
|
-
};
|
|
12562
12812
|
}
|
|
12563
|
-
function
|
|
12564
|
-
|
|
12565
|
-
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
void 0,
|
|
12572
|
-
operator
|
|
12573
|
-
);
|
|
12813
|
+
function hasAggregateInStringFuncArg(arg) {
|
|
12814
|
+
if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
|
|
12815
|
+
return scalarValueHasAggregate2(arg);
|
|
12816
|
+
}
|
|
12817
|
+
function scalarValueHasAggregate2(expr) {
|
|
12818
|
+
if (expr.type === "STRING_FUNC") return hasAggregateInStringFuncExpr2(expr);
|
|
12819
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
12820
|
+
return scalarValueHasAggregate2(expr.left) || scalarValueHasAggregate2(expr.right);
|
|
12574
12821
|
}
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
return relativeDateUnsupported(
|
|
12578
|
-
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
12579
|
-
functionName,
|
|
12580
|
-
left.field,
|
|
12581
|
-
void 0,
|
|
12582
|
-
operator
|
|
12583
|
-
);
|
|
12822
|
+
if (expr.type === "CASE_WHEN") {
|
|
12823
|
+
return expr.branches.some((branch) => caseResultHasAggregate2(branch.result)) || expr.elseResult !== null && caseResultHasAggregate2(expr.elseResult);
|
|
12584
12824
|
}
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12825
|
+
return false;
|
|
12826
|
+
}
|
|
12827
|
+
function caseResultHasAggregate2(result) {
|
|
12828
|
+
if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
|
|
12829
|
+
return scalarValueHasAggregate2(result);
|
|
12830
|
+
}
|
|
12831
|
+
function hasAggregateInStringFuncExpr2(expr) {
|
|
12832
|
+
return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
|
|
12833
|
+
}
|
|
12834
|
+
function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
|
|
12835
|
+
if (arg.type === "AGG_REF") {
|
|
12836
|
+
const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
|
|
12837
|
+
return typeof value === "number" ? { type: "NUMBER", value, raw: String(value) } : { type: "STRING", value };
|
|
12593
12838
|
}
|
|
12594
|
-
if (
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
functionName,
|
|
12598
|
-
left.field,
|
|
12599
|
-
semantics.fieldType,
|
|
12600
|
-
operator
|
|
12601
|
-
);
|
|
12839
|
+
if (arg.type === "AGG_ARITH") {
|
|
12840
|
+
const value = evalAggArithExpr(arg, rows, resolveAggSortKind);
|
|
12841
|
+
return { type: "NUMBER", value, raw: String(value) };
|
|
12602
12842
|
}
|
|
12603
|
-
if (
|
|
12604
|
-
return
|
|
12605
|
-
"WHERE_RELATIVE_DATE_FIELD_TYPE_UNSUPPORTED",
|
|
12606
|
-
functionName,
|
|
12607
|
-
left.field,
|
|
12608
|
-
semantics.fieldType,
|
|
12609
|
-
operator
|
|
12610
|
-
);
|
|
12843
|
+
if (arg.type === "STRING_FUNC") {
|
|
12844
|
+
return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
|
|
12611
12845
|
}
|
|
12612
|
-
return
|
|
12613
|
-
capability: "EXACT_PUSHDOWN",
|
|
12614
|
-
reasons: [{
|
|
12615
|
-
code: "WHERE_EXACT",
|
|
12616
|
-
functionName,
|
|
12617
|
-
field: left.field,
|
|
12618
|
-
fieldType: semantics.fieldType,
|
|
12619
|
-
operator
|
|
12620
|
-
}]
|
|
12621
|
-
};
|
|
12846
|
+
return resolveAggInScalarValue(arg, rows, resolveAggSortKind);
|
|
12622
12847
|
}
|
|
12623
|
-
function
|
|
12624
|
-
if (
|
|
12625
|
-
|
|
12626
|
-
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
return value.args.kind === "NONE";
|
|
12632
|
-
case "FROM_TODAY":
|
|
12633
|
-
return value.args.kind === "FROM_TODAY" && Number.isSafeInteger(value.args.offset) && value.args.offsetText === String(value.args.offset === 0 ? 0 : value.args.offset) && (value.args.unit === "DAYS" || value.args.unit === "WEEKS" || value.args.unit === "MONTHS" || value.args.unit === "YEARS");
|
|
12634
|
-
case "THIS_WEEK":
|
|
12635
|
-
case "LAST_WEEK":
|
|
12636
|
-
case "NEXT_WEEK":
|
|
12637
|
-
return value.args.kind === "WEEK" && (value.args.weekday === null || value.args.weekday === "SUNDAY" || value.args.weekday === "MONDAY" || value.args.weekday === "TUESDAY" || value.args.weekday === "WEDNESDAY" || value.args.weekday === "THURSDAY" || value.args.weekday === "FRIDAY" || value.args.weekday === "SATURDAY");
|
|
12638
|
-
case "THIS_MONTH":
|
|
12639
|
-
case "LAST_MONTH":
|
|
12640
|
-
case "NEXT_MONTH":
|
|
12641
|
-
return value.args.kind === "MONTH" && (value.args.day === null || value.args.day === "LAST" || Number.isInteger(value.args.day) && value.args.day >= 1 && value.args.day <= 31);
|
|
12642
|
-
default:
|
|
12643
|
-
return false;
|
|
12848
|
+
function resolveAggInScalarValue(expr, rows, resolveAggSortKind) {
|
|
12849
|
+
if (expr.type === "STRING_FUNC") return resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind);
|
|
12850
|
+
if (expr.type === "SCALAR_ARITH" || expr.type === "CONCAT_OP") {
|
|
12851
|
+
return {
|
|
12852
|
+
...expr,
|
|
12853
|
+
left: resolveAggInScalarValue(expr.left, rows, resolveAggSortKind),
|
|
12854
|
+
right: resolveAggInScalarValue(expr.right, rows, resolveAggSortKind)
|
|
12855
|
+
};
|
|
12644
12856
|
}
|
|
12857
|
+
return expr;
|
|
12645
12858
|
}
|
|
12646
|
-
function
|
|
12647
|
-
const semantics = resolveField2(field);
|
|
12648
|
-
if (!semantics) return unsupported2("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
|
|
12649
|
-
if (!isLocallyValidOperator(semantics.fieldType, operator)) {
|
|
12650
|
-
return unsupported2(
|
|
12651
|
-
"WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE",
|
|
12652
|
-
field.field,
|
|
12653
|
-
semantics.fieldType,
|
|
12654
|
-
operator
|
|
12655
|
-
);
|
|
12656
|
-
}
|
|
12657
|
-
if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
|
|
12658
|
-
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
|
|
12659
|
-
}
|
|
12859
|
+
function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
|
|
12660
12860
|
return {
|
|
12661
|
-
|
|
12662
|
-
|
|
12861
|
+
type: "STRING_FUNC",
|
|
12862
|
+
func: expr.func,
|
|
12863
|
+
args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
|
|
12663
12864
|
};
|
|
12664
12865
|
}
|
|
12665
|
-
function
|
|
12666
|
-
|
|
12667
|
-
|
|
12866
|
+
function stripHiddenQualifiedColumns(row, hiddenQualifiedAliases) {
|
|
12867
|
+
if (!hiddenQualifiedAliases || hiddenQualifiedAliases.size === 0) return row;
|
|
12868
|
+
const out = {};
|
|
12869
|
+
for (const [key, value] of Object.entries(row)) {
|
|
12870
|
+
if ([...hiddenQualifiedAliases].some((alias) => key.startsWith(`${alias}.`))) continue;
|
|
12871
|
+
out[key] = value;
|
|
12872
|
+
}
|
|
12873
|
+
return out;
|
|
12668
12874
|
}
|
|
12669
|
-
function
|
|
12670
|
-
if (
|
|
12671
|
-
|
|
12672
|
-
return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
|
|
12875
|
+
function flattenedColumns(columns, alias) {
|
|
12876
|
+
if (columns === void 0) return void 0;
|
|
12877
|
+
return alias ? columns.flatMap((column) => [`${alias}.${column}`, column]) : [...columns];
|
|
12673
12878
|
}
|
|
12674
|
-
function
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12879
|
+
function mergeKnownColumns(left, right, rows) {
|
|
12880
|
+
if (left === void 0 && right === void 0 && rows.length === 0) return void 0;
|
|
12881
|
+
return [.../* @__PURE__ */ new Set([
|
|
12882
|
+
...left ?? [],
|
|
12883
|
+
...right ?? [],
|
|
12884
|
+
...Object.keys(rows[0] ?? {})
|
|
12885
|
+
])];
|
|
12886
|
+
}
|
|
12887
|
+
function deriveOutputOrderSemantics(columns) {
|
|
12888
|
+
const result = /* @__PURE__ */ new Map();
|
|
12889
|
+
for (const column of columns) {
|
|
12890
|
+
if (!("alias" in column) || !column.alias) continue;
|
|
12891
|
+
if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
|
|
12892
|
+
result.set(column.alias, syntheticSemantics("number"));
|
|
12893
|
+
} else if (column.type === "AGGREGATE") {
|
|
12894
|
+
if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG" || column.func === "STDDEV_POP" || column.func === "STDDEV_SAMP" || column.func === "VAR_POP" || column.func === "VAR_SAMP" || column.func === "MEDIAN") {
|
|
12895
|
+
result.set(column.alias, syntheticSemantics("number"));
|
|
12896
|
+
} else if (column.func === "GROUP_CONCAT") {
|
|
12897
|
+
result.set(column.alias, syntheticSemantics("string"));
|
|
12898
|
+
}
|
|
12899
|
+
} else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "SCALAR_VALUE_COL") {
|
|
12900
|
+
result.set(column.alias, syntheticSemantics("string"));
|
|
12901
|
+
} else if (column.type === "STRFUNC_COL") {
|
|
12902
|
+
result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
|
|
12903
|
+
}
|
|
12690
12904
|
}
|
|
12905
|
+
return result;
|
|
12691
12906
|
}
|
|
12692
|
-
function
|
|
12693
|
-
const
|
|
12694
|
-
|
|
12695
|
-
|
|
12907
|
+
function runFullScan(input) {
|
|
12908
|
+
const {
|
|
12909
|
+
stmt,
|
|
12910
|
+
tables,
|
|
12911
|
+
scalarCache,
|
|
12912
|
+
optionOrders,
|
|
12913
|
+
sortKinds,
|
|
12914
|
+
orderSemantics,
|
|
12915
|
+
fieldTypeResolver,
|
|
12916
|
+
fieldSemanticsResolver,
|
|
12917
|
+
havingFieldTypeResolver,
|
|
12918
|
+
havingFieldSemanticsResolver,
|
|
12919
|
+
aggregateSortKindResolver,
|
|
12920
|
+
appliedKlikes,
|
|
12921
|
+
sourceColumns: sourceColumns2,
|
|
12922
|
+
tableColumns,
|
|
12923
|
+
hiddenQualifiedAliases,
|
|
12924
|
+
resolvedGroupingSpec,
|
|
12925
|
+
plainGroupByPlan
|
|
12926
|
+
} = input;
|
|
12927
|
+
const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
|
|
12928
|
+
for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
|
|
12929
|
+
let rows = [];
|
|
12930
|
+
const mainAlias = stmt.from.alias;
|
|
12931
|
+
const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
|
|
12932
|
+
rows = mainRecords.map((r) => flatten(r, mainAlias));
|
|
12933
|
+
let knownColumns = mergeKnownColumns(
|
|
12934
|
+
flattenedColumns(tableColumns?.get(mainAlias), mainAlias),
|
|
12935
|
+
void 0,
|
|
12936
|
+
rows
|
|
12937
|
+
);
|
|
12938
|
+
for (const join2 of stmt.joins) {
|
|
12939
|
+
const rightAlias = join2.table.alias;
|
|
12940
|
+
const rightRecords = tables.get(rightAlias) ?? [];
|
|
12941
|
+
const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
|
|
12942
|
+
const rightColumns = mergeKnownColumns(
|
|
12943
|
+
flattenedColumns(tableColumns?.get(rightAlias), rightAlias),
|
|
12944
|
+
void 0,
|
|
12945
|
+
rightRows
|
|
12946
|
+
);
|
|
12947
|
+
rows = applyJoin(rows, rightRows, join2, {
|
|
12948
|
+
leftColumns: knownColumns,
|
|
12949
|
+
rightColumns
|
|
12950
|
+
});
|
|
12951
|
+
knownColumns = mergeKnownColumns(knownColumns, rightColumns, rows);
|
|
12696
12952
|
}
|
|
12697
|
-
|
|
12698
|
-
|
|
12953
|
+
const filterWhere = input.residualWhere !== void 0 ? input.residualWhere : stmt.where;
|
|
12954
|
+
rows = applyFilter(rows, filterWhere, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
|
|
12955
|
+
const grouping = normalizeGroupingSpec(stmt);
|
|
12956
|
+
if (grouping.type === "GROUPING_SETS") {
|
|
12957
|
+
if (!resolvedGroupingSpec) {
|
|
12958
|
+
throw new Error("internal error: B65 grouping sets require a metadata-resolved grouping spec.");
|
|
12959
|
+
}
|
|
12960
|
+
rows = applyGroupingSets(
|
|
12961
|
+
rows,
|
|
12962
|
+
resolvedGroupingSpec,
|
|
12963
|
+
stmt.columns,
|
|
12964
|
+
aggregateSortKindResolver,
|
|
12965
|
+
{ maxGeneratedRows: B65_MAX_GENERATED_ROWS }
|
|
12966
|
+
);
|
|
12967
|
+
} else if (grouping.type === "PLAIN" || hasAggregateColumns(stmt.columns)) {
|
|
12968
|
+
rows = applyGroupBy(
|
|
12969
|
+
rows,
|
|
12970
|
+
grouping.type === "PLAIN" ? grouping.allItems : [],
|
|
12971
|
+
stmt.columns,
|
|
12972
|
+
aggregateSortKindResolver,
|
|
12973
|
+
grouping.type === "PLAIN" ? plainGroupByPlan : void 0,
|
|
12974
|
+
{
|
|
12975
|
+
scalarCache,
|
|
12976
|
+
resolveFieldType: fieldTypeResolver,
|
|
12977
|
+
resolveFieldSemantics: fieldSemanticsResolver
|
|
12978
|
+
}
|
|
12979
|
+
);
|
|
12699
12980
|
}
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12981
|
+
rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
|
|
12982
|
+
rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
|
|
12983
|
+
if (stmt.distinct) {
|
|
12984
|
+
rows = applyDistinct(
|
|
12985
|
+
rows,
|
|
12986
|
+
stmt.columns,
|
|
12987
|
+
scalarCache,
|
|
12988
|
+
fieldTypeResolver,
|
|
12989
|
+
fieldSemanticsResolver
|
|
12990
|
+
);
|
|
12705
12991
|
}
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12992
|
+
rows = applyOrderBy(
|
|
12993
|
+
rows,
|
|
12994
|
+
stmt.orderBy,
|
|
12995
|
+
optionOrders,
|
|
12996
|
+
sortKinds,
|
|
12997
|
+
effectiveOrderSemantics,
|
|
12998
|
+
buildOrderByAliasEvaluator(stmt.columns, scalarCache, fieldTypeResolver, fieldSemanticsResolver)
|
|
12999
|
+
);
|
|
13000
|
+
rows = applyLimit(rows, stmt.limit, stmt.offset);
|
|
13001
|
+
return project(
|
|
13002
|
+
rows,
|
|
13003
|
+
stmt.columns,
|
|
13004
|
+
scalarCache,
|
|
13005
|
+
fieldTypeResolver,
|
|
13006
|
+
sourceColumns2,
|
|
13007
|
+
fieldSemanticsResolver,
|
|
13008
|
+
hiddenQualifiedAliases
|
|
13009
|
+
);
|
|
12710
13010
|
}
|
|
12711
|
-
|
|
12712
|
-
|
|
13011
|
+
|
|
13012
|
+
// src/converter/subtableAdapter.ts
|
|
13013
|
+
function expandSubtableRecords(parents, subtableCode) {
|
|
13014
|
+
const rows = [];
|
|
13015
|
+
for (const parent of parents) {
|
|
13016
|
+
const parentId = toFlatString(parent["$id"]?.value);
|
|
13017
|
+
const tableValue = readTableRows(parent[subtableCode]?.value);
|
|
13018
|
+
if (tableValue.length === 0) continue;
|
|
13019
|
+
for (let i = 0; i < tableValue.length; i++) {
|
|
13020
|
+
const row = tableValue[i];
|
|
13021
|
+
const out = {
|
|
13022
|
+
_pid: { value: parentId },
|
|
13023
|
+
_rid: { value: row.id ?? "" },
|
|
13024
|
+
_idx: { value: String(i) }
|
|
13025
|
+
};
|
|
13026
|
+
for (const [field, fieldValue] of Object.entries(parent)) {
|
|
13027
|
+
if (field === subtableCode) continue;
|
|
13028
|
+
const flat = toFlatString(fieldValue?.value);
|
|
13029
|
+
out[`_p.${field}`] = { value: flat };
|
|
13030
|
+
}
|
|
13031
|
+
for (const [field, cell] of Object.entries(row.value ?? {})) {
|
|
13032
|
+
out[field] = { value: toFlatString(cell?.value) };
|
|
13033
|
+
}
|
|
13034
|
+
rows.push(out);
|
|
13035
|
+
}
|
|
13036
|
+
}
|
|
13037
|
+
return rows;
|
|
12713
13038
|
}
|
|
12714
|
-
function
|
|
12715
|
-
return
|
|
12716
|
-
|
|
12717
|
-
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12718
|
-
});
|
|
13039
|
+
function readTableRows(value) {
|
|
13040
|
+
if (!Array.isArray(value)) return [];
|
|
13041
|
+
return value.filter((item) => !!item && typeof item === "object");
|
|
12719
13042
|
}
|
|
12720
|
-
function
|
|
12721
|
-
return
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
13043
|
+
function toFlatString(value) {
|
|
13044
|
+
if (value === null || value === void 0) return "";
|
|
13045
|
+
if (typeof value === "string") return value;
|
|
13046
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
13047
|
+
try {
|
|
13048
|
+
return JSON.stringify(value);
|
|
13049
|
+
} catch {
|
|
13050
|
+
return String(value);
|
|
13051
|
+
}
|
|
12725
13052
|
}
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
13053
|
+
|
|
13054
|
+
// src/core/dmlPrevalidation.ts
|
|
13055
|
+
function collectDmlPrevalidationSnapshotFields(fieldIndex) {
|
|
13056
|
+
return [
|
|
13057
|
+
"$id",
|
|
13058
|
+
.../* @__PURE__ */ new Set([
|
|
13059
|
+
...fieldIndex.topLevel.map((field) => field.code).filter((code) => code !== "$id" && code !== "$revision"),
|
|
13060
|
+
...fieldIndex.subtables.keys()
|
|
13061
|
+
])
|
|
13062
|
+
];
|
|
12730
13063
|
}
|
|
12731
|
-
function
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
13064
|
+
function buildDmlValidationPostImage(snapshot, sparseRecord) {
|
|
13065
|
+
const postImage = deepClone(snapshot);
|
|
13066
|
+
for (const [field, cell] of Object.entries(sparseRecord)) {
|
|
13067
|
+
postImage[field] = deepClone(cell);
|
|
13068
|
+
}
|
|
13069
|
+
return postImage;
|
|
12735
13070
|
}
|
|
12736
|
-
function
|
|
12737
|
-
|
|
12738
|
-
|
|
13071
|
+
function mergeDmlCandidateValidation(input) {
|
|
13072
|
+
const errors = [
|
|
13073
|
+
...input.preErrors.map((row) => normalizePlainError(row)),
|
|
13074
|
+
...input.postImageErrors.map((row) => normalizePlainError(row, row["$err_subtable"] !== "")),
|
|
13075
|
+
...input.checkErrors.map((row) => normalizePlainError(row))
|
|
13076
|
+
];
|
|
13077
|
+
const invalidRowNumbers = /* @__PURE__ */ new Set();
|
|
13078
|
+
if (errors.length > 0) invalidRowNumbers.add(input.rowNumber);
|
|
13079
|
+
const writeRecord = {};
|
|
13080
|
+
for (const field of new Set(input.setFields)) {
|
|
13081
|
+
if (!Object.prototype.hasOwnProperty.call(input.normalizedPostImage, field)) {
|
|
13082
|
+
throw new Error(`InternalError: normalized post-image is missing SET field: ${field}`);
|
|
13083
|
+
}
|
|
13084
|
+
writeRecord[field] = deepClone(input.normalizedPostImage[field]);
|
|
12739
13085
|
}
|
|
12740
|
-
const relative = result.reasons.find(
|
|
12741
|
-
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
12742
|
-
);
|
|
12743
13086
|
return {
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
code: "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN",
|
|
12749
|
-
functionName: relative.functionName,
|
|
12750
|
-
field: relative.field,
|
|
12751
|
-
fieldType: relative.fieldType,
|
|
12752
|
-
operator: relative.operator
|
|
12753
|
-
}
|
|
12754
|
-
]
|
|
13087
|
+
errors,
|
|
13088
|
+
invalidRows: invalidRowNumbers.size,
|
|
13089
|
+
invalidRowNumbers,
|
|
13090
|
+
writeRecord
|
|
12755
13091
|
};
|
|
12756
13092
|
}
|
|
12757
|
-
function
|
|
12758
|
-
if (result.capability === "EXACT_PUSHDOWN" || !hasLegacyKintoneFunctionReason(result.reasons) || result.reasons.some(
|
|
12759
|
-
(reason) => reason.code === "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN"
|
|
12760
|
-
)) {
|
|
12761
|
-
return result;
|
|
12762
|
-
}
|
|
12763
|
-
const legacy = result.reasons.find(
|
|
12764
|
-
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
12765
|
-
);
|
|
13093
|
+
function normalizePlainError(row, childCell = false) {
|
|
12766
13094
|
return {
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
functionName: legacy.functionName,
|
|
12773
|
-
field: legacy.field,
|
|
12774
|
-
fieldType: legacy.fieldType,
|
|
12775
|
-
operator: legacy.operator
|
|
12776
|
-
}
|
|
12777
|
-
]
|
|
13095
|
+
...row,
|
|
13096
|
+
$err_value: childCell ? row["$err_value"] ?? "" : "",
|
|
13097
|
+
$err_subtable: childCell ? row["$err_subtable"] ?? "" : "",
|
|
13098
|
+
$err_subrow: childCell ? row["$err_subrow"] ?? "" : "",
|
|
13099
|
+
$err_subrow_id: childCell ? row["$err_subrow_id"] ?? "" : ""
|
|
12778
13100
|
};
|
|
12779
13101
|
}
|
|
12780
|
-
function
|
|
12781
|
-
return
|
|
12782
|
-
|
|
12783
|
-
);
|
|
13102
|
+
function deepClone(value, seen = /* @__PURE__ */ new Map()) {
|
|
13103
|
+
if (value === null || typeof value !== "object") return value;
|
|
13104
|
+
const object = value;
|
|
13105
|
+
const existing = seen.get(object);
|
|
13106
|
+
if (existing !== void 0) return existing;
|
|
13107
|
+
if (Array.isArray(value)) {
|
|
13108
|
+
const clone2 = [];
|
|
13109
|
+
seen.set(object, clone2);
|
|
13110
|
+
for (const item of value) clone2.push(deepClone(item, seen));
|
|
13111
|
+
return clone2;
|
|
13112
|
+
}
|
|
13113
|
+
const clone = {};
|
|
13114
|
+
seen.set(object, clone);
|
|
13115
|
+
for (const [key, child] of Object.entries(value)) clone[key] = deepClone(child, seen);
|
|
13116
|
+
return clone;
|
|
12784
13117
|
}
|
|
12785
13118
|
|
|
12786
13119
|
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
@@ -16320,6 +16653,46 @@ function extractMainTypedPushdownCandidate(stmt) {
|
|
|
16320
16653
|
if (!stmt.from.alias) return null;
|
|
16321
16654
|
return extractTypedPushdownCandidates(stmt.where, { tableAlias: stmt.from.alias });
|
|
16322
16655
|
}
|
|
16656
|
+
function buildRuntimeJoinPushdownPlan(stmt, metadata) {
|
|
16657
|
+
if (stmt.joins.length === 0 || stmt.where === null || stmt.joins.some((join2) => join2.type !== "INNER")) {
|
|
16658
|
+
return null;
|
|
16659
|
+
}
|
|
16660
|
+
const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
|
|
16661
|
+
if (tables.some(
|
|
16662
|
+
(table) => table.alias === null || table.cteName !== null || Boolean(table.subtableCode)
|
|
16663
|
+
)) {
|
|
16664
|
+
return null;
|
|
16665
|
+
}
|
|
16666
|
+
const sources = tables.map((table) => ({
|
|
16667
|
+
alias: table.alias,
|
|
16668
|
+
appId: table.appId,
|
|
16669
|
+
sourceKind: "APP",
|
|
16670
|
+
fieldTypes: new Map([
|
|
16671
|
+
...metadata.fieldTypesByApp.get(table.appId) ?? /* @__PURE__ */ new Map(),
|
|
16672
|
+
["$id", "__ID__"]
|
|
16673
|
+
]),
|
|
16674
|
+
fieldOptions: metadata.fieldOptionsByApp.get(table.appId)
|
|
16675
|
+
}));
|
|
16676
|
+
const plan = buildJoinPushdownPlan(stmt.where, sources);
|
|
16677
|
+
const conditionsByAlias = /* @__PURE__ */ new Map();
|
|
16678
|
+
const queriesByAlias = /* @__PURE__ */ new Map();
|
|
16679
|
+
for (const item of plan.items) {
|
|
16680
|
+
queriesByAlias.set(item.targetAlias, serializeJoinPushdownItem(item, sources));
|
|
16681
|
+
conditionsByAlias.set(item.targetAlias, item.predicate);
|
|
16682
|
+
}
|
|
16683
|
+
const mainAlias = stmt.from.alias;
|
|
16684
|
+
const mainCondition = conditionsByAlias.get(mainAlias) ?? null;
|
|
16685
|
+
const joinConditions = new Map(conditionsByAlias);
|
|
16686
|
+
joinConditions.delete(mainAlias);
|
|
16687
|
+
return {
|
|
16688
|
+
joinPlan: plan,
|
|
16689
|
+
queriesByAlias,
|
|
16690
|
+
mainCondition,
|
|
16691
|
+
joinConditions,
|
|
16692
|
+
appliedKlikes: plan.appliedKlikes,
|
|
16693
|
+
allKlikes: plan.allKlikes
|
|
16694
|
+
};
|
|
16695
|
+
}
|
|
16323
16696
|
async function loadTypedPushdownMeta(stmt, client, cacheContext) {
|
|
16324
16697
|
const candidatesByApp = /* @__PURE__ */ new Map();
|
|
16325
16698
|
const addCandidate = (appId, candidate) => {
|
|
@@ -16329,6 +16702,13 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
|
|
|
16329
16702
|
else candidatesByApp.set(appId, [candidate]);
|
|
16330
16703
|
};
|
|
16331
16704
|
addCandidate(stmt.from.appId, extractMainTypedPushdownCandidate(stmt));
|
|
16705
|
+
const directInnerJoin = stmt.joins.length > 0 && stmt.where !== null && stmt.joins.every((join2) => join2.type === "INNER") && [stmt.from, ...stmt.joins.map((join2) => join2.table)].every(
|
|
16706
|
+
(table) => table.alias !== null && table.cteName === null && !table.subtableCode
|
|
16707
|
+
);
|
|
16708
|
+
if (directInnerJoin) {
|
|
16709
|
+
addCandidate(stmt.from.appId, stmt.where);
|
|
16710
|
+
for (const join2 of stmt.joins) addCandidate(join2.table.appId, stmt.where);
|
|
16711
|
+
}
|
|
16332
16712
|
if (stmt.where !== null) {
|
|
16333
16713
|
for (const join2 of stmt.joins) {
|
|
16334
16714
|
if (!join2.table.alias || join2.table.subtableCode || join2.table.cteName !== null) continue;
|
|
@@ -16939,8 +17319,9 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16939
17319
|
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
16940
17320
|
);
|
|
16941
17321
|
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
16942
|
-
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
17322
|
+
const pushdownPlan = buildRuntimeJoinPushdownPlan(stmt, pushdownMeta) ?? buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
16943
17323
|
validateKlikePushdownPlan(pushdownPlan);
|
|
17324
|
+
const fetchClient = client;
|
|
16944
17325
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
16945
17326
|
const tableConditions = pushdownPlan.joinConditions;
|
|
16946
17327
|
if (prefilterPlan && allowOriginalWherePushdown) {
|
|
@@ -16952,7 +17333,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16952
17333
|
const mainFetch = constantFalse ? Promise.resolve([]) : fetchTableRecordsForFullScan(
|
|
16953
17334
|
stmt,
|
|
16954
17335
|
stmt.from,
|
|
16955
|
-
|
|
17336
|
+
fetchClient,
|
|
16956
17337
|
maxRecords,
|
|
16957
17338
|
parallel,
|
|
16958
17339
|
true,
|
|
@@ -16976,7 +17357,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
16976
17357
|
promise: fetchTableRecordsForFullScan(
|
|
16977
17358
|
stmt,
|
|
16978
17359
|
join2.table,
|
|
16979
|
-
|
|
17360
|
+
fetchClient,
|
|
16980
17361
|
maxRecords,
|
|
16981
17362
|
parallel,
|
|
16982
17363
|
false,
|
|
@@ -17005,18 +17386,19 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17005
17386
|
stmt,
|
|
17006
17387
|
join2,
|
|
17007
17388
|
tables,
|
|
17008
|
-
|
|
17389
|
+
fetchClient,
|
|
17009
17390
|
maxRecords,
|
|
17010
17391
|
parallel,
|
|
17011
17392
|
options.onLimitReached ?? "error",
|
|
17012
17393
|
warnings,
|
|
17013
17394
|
null,
|
|
17014
|
-
plainGroupByPlan
|
|
17395
|
+
plainGroupByPlan,
|
|
17396
|
+
""
|
|
17015
17397
|
);
|
|
17016
17398
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
17017
17399
|
stmt,
|
|
17018
17400
|
join2.table,
|
|
17019
|
-
|
|
17401
|
+
fetchClient,
|
|
17020
17402
|
maxRecords,
|
|
17021
17403
|
parallel,
|
|
17022
17404
|
false,
|
|
@@ -17024,7 +17406,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
|
|
|
17024
17406
|
warnings,
|
|
17025
17407
|
null,
|
|
17026
17408
|
true,
|
|
17027
|
-
plainGroupByPlan
|
|
17409
|
+
plainGroupByPlan,
|
|
17410
|
+
""
|
|
17028
17411
|
);
|
|
17029
17412
|
tables.set(join2.table.alias, joinRecords);
|
|
17030
17413
|
}));
|
|
@@ -17212,8 +17595,9 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
17212
17595
|
whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
|
|
17213
17596
|
);
|
|
17214
17597
|
const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
|
|
17215
|
-
const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
17598
|
+
const pushdownPlan = buildRuntimeJoinPushdownPlan(stmt, pushdownMeta) ?? buildKlikePushdownPlan(stmt, pushdownMeta);
|
|
17216
17599
|
validateKlikePushdownPlan(pushdownPlan);
|
|
17600
|
+
const fetchClient = client;
|
|
17217
17601
|
const scalarCache = await resolveScalarColumns(
|
|
17218
17602
|
stmt.columns,
|
|
17219
17603
|
client,
|
|
@@ -17234,7 +17618,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
17234
17618
|
const mainRecords = await withCompleteInputPolicy(completePolicy, () => fetchTableRecordsForFullScan(
|
|
17235
17619
|
stmt,
|
|
17236
17620
|
stmt.from,
|
|
17237
|
-
|
|
17621
|
+
fetchClient,
|
|
17238
17622
|
maxRecords,
|
|
17239
17623
|
parallel,
|
|
17240
17624
|
true,
|
|
@@ -17257,7 +17641,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
17257
17641
|
stmt,
|
|
17258
17642
|
join2,
|
|
17259
17643
|
tables,
|
|
17260
|
-
|
|
17644
|
+
fetchClient,
|
|
17261
17645
|
maxRecords,
|
|
17262
17646
|
parallel,
|
|
17263
17647
|
effectiveOptions.onLimitReached ?? "error",
|
|
@@ -17268,7 +17652,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
|
|
|
17268
17652
|
const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
|
|
17269
17653
|
stmt,
|
|
17270
17654
|
join2.table,
|
|
17271
|
-
|
|
17655
|
+
fetchClient,
|
|
17272
17656
|
maxRecords,
|
|
17273
17657
|
parallel,
|
|
17274
17658
|
false,
|
|
@@ -17310,14 +17694,18 @@ function processRowToKintoneRecord(row) {
|
|
|
17310
17694
|
Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
|
|
17311
17695
|
);
|
|
17312
17696
|
}
|
|
17313
|
-
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true, plainGroupByPlan) {
|
|
17697
|
+
async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true, plainGroupByPlan, additionalPushQuery = "") {
|
|
17314
17698
|
const fields = selectToFetchAllFields(stmt, table, plainGroupByPlan);
|
|
17315
17699
|
const onTruncate = (max) => {
|
|
17316
17700
|
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`);
|
|
17317
17701
|
};
|
|
17318
17702
|
if (!table.subtableCode) {
|
|
17319
17703
|
const baseQuery = isMainTable && allowOriginalWherePushdown ? selectToFetchAllParams(stmt, table.appId).query : "";
|
|
17320
|
-
const
|
|
17704
|
+
const pushQueries = [
|
|
17705
|
+
pushDownCond !== null ? whereToKintone(pushDownCond) : "",
|
|
17706
|
+
additionalPushQuery
|
|
17707
|
+
].filter((query2) => query2 !== "");
|
|
17708
|
+
const pushQuery = pushQueries.length > 1 ? pushQueries.map((query2) => `(${query2})`).join(" and ") : pushQueries[0] ?? "";
|
|
17321
17709
|
const query = baseQuery && pushQuery ? `(${baseQuery}) and (${pushQuery})` : baseQuery || pushQuery;
|
|
17322
17710
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, table.appId, query, fields, {
|
|
17323
17711
|
parallel,
|
|
@@ -17425,7 +17813,7 @@ function splitChunks(items, size) {
|
|
|
17425
17813
|
var JOIN_IN_CHUNK_SIZE = 50;
|
|
17426
17814
|
var JOIN_IN_MAX_CHUNKS = 6;
|
|
17427
17815
|
var JOIN_IN_MAX_KEYS = JOIN_IN_CHUNK_SIZE * JOIN_IN_MAX_CHUNKS;
|
|
17428
|
-
async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxRecords, parallel, onLimit, warnings, pushDownCond = null, plainGroupByPlan) {
|
|
17816
|
+
async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxRecords, parallel, onLimit, warnings, pushDownCond = null, plainGroupByPlan, additionalPushQuery = "") {
|
|
17429
17817
|
if (join2.type !== "INNER") return null;
|
|
17430
17818
|
if (!join2.table.alias) return null;
|
|
17431
17819
|
if (join2.table.subtableCode) return null;
|
|
@@ -17471,7 +17859,14 @@ async function tryFetchJoinRecordsBySourceKeys(stmt, join2, tables, client, maxR
|
|
|
17471
17859
|
const seen = /* @__PURE__ */ new Set();
|
|
17472
17860
|
for (const chunk3 of chunks) {
|
|
17473
17861
|
const inClause = `${joinField} in (${chunk3.map(sqlQuote).join(",")})`;
|
|
17474
|
-
const
|
|
17862
|
+
const pushQueries = [
|
|
17863
|
+
pushDownCond !== null ? whereToKintone(pushDownCond) : "",
|
|
17864
|
+
additionalPushQuery
|
|
17865
|
+
].filter((query2) => query2 !== "");
|
|
17866
|
+
const query = pushQueries.reduce(
|
|
17867
|
+
(combined, pushQuery) => `(${combined}) and (${pushQuery})`,
|
|
17868
|
+
inClause
|
|
17869
|
+
);
|
|
17475
17870
|
const resolved = await fetchRecordsForSharedPlan(client.getRecords, join2.table.appId, query, fields, {
|
|
17476
17871
|
parallel,
|
|
17477
17872
|
maxRecords,
|
|
@@ -20676,6 +21071,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
|
|
|
20676
21071
|
pending.forEach(([i], idx) => cache.set(i, values[idx]));
|
|
20677
21072
|
return cache;
|
|
20678
21073
|
}
|
|
21074
|
+
var explainJoinPushdownPlans = /* @__PURE__ */ new WeakMap();
|
|
20679
21075
|
var validateExplainInfo = /* @__PURE__ */ new WeakMap();
|
|
20680
21076
|
var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
|
|
20681
21077
|
async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4, relativeDatePlan) {
|
|
@@ -20737,6 +21133,9 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
|
|
|
20737
21133
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
|
|
20738
21134
|
}
|
|
20739
21135
|
capabilities.set(select, capability);
|
|
21136
|
+
const joinPushdownMeta = await loadTypedPushdownMeta(select, tracedClient, cacheContext);
|
|
21137
|
+
const joinPushdownPlan = buildRuntimeJoinPushdownPlan(select, joinPushdownMeta);
|
|
21138
|
+
if (joinPushdownPlan) explainJoinPushdownPlans.set(select, joinPushdownPlan);
|
|
20740
21139
|
if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
|
|
20741
21140
|
const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
|
|
20742
21141
|
if (select.orderMode !== "KINTONE_NATIVE") {
|
|
@@ -21477,7 +21876,26 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21477
21876
|
lines.push(` kintone query: ${displayedQuery || "(\u306A\u3057)"}`);
|
|
21478
21877
|
lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
|
|
21479
21878
|
} else {
|
|
21480
|
-
const
|
|
21879
|
+
const runtimeJoinPlan = explainJoinPushdownPlans.get(stmt);
|
|
21880
|
+
const pushdownPlan = runtimeJoinPlan ?? buildKlikePushdownPlan(stmt);
|
|
21881
|
+
if (stmt.joins.length > 0) {
|
|
21882
|
+
if (runtimeJoinPlan) {
|
|
21883
|
+
lines.push(" join pushdown plan: applied (runtime metadata resolved)");
|
|
21884
|
+
lines.push(" runtime plan timing: variables/subqueries resolved -> metadata resolved -> immutable plan");
|
|
21885
|
+
lines.push(" EXPLAIN unresolved subqueries: not applied (records API is not called)");
|
|
21886
|
+
lines.push(" residual: original WHERE");
|
|
21887
|
+
lines.push(` KLIKE applied nodes: ${runtimeJoinPlan.joinPlan.appliedKlikes.size}`);
|
|
21888
|
+
for (const rejection of runtimeJoinPlan.joinPlan.rejections) {
|
|
21889
|
+
lines.push(` join pushdown not applied: ${rejection.reason}`);
|
|
21890
|
+
}
|
|
21891
|
+
} else {
|
|
21892
|
+
const reason = stmt.joins.some((join2) => join2.type !== "INNER") ? "OUTER_JOIN" : [stmt.from, ...stmt.joins.map((join2) => join2.table)].some(
|
|
21893
|
+
(table) => table.cteName !== null || Boolean(table.subtableCode)
|
|
21894
|
+
) ? "SOURCE_KIND" : "PLAN_NOT_APPLICABLE";
|
|
21895
|
+
lines.push(" join pushdown plan: not applied");
|
|
21896
|
+
lines.push(` join pushdown not applied: ${reason}`);
|
|
21897
|
+
}
|
|
21898
|
+
}
|
|
21481
21899
|
const mainFields = selectToFetchAllFields(stmt, stmt.from, plainGroupByPlan);
|
|
21482
21900
|
const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
|
|
21483
21901
|
const mainPushDown = pushdownPlan.mainCondition;
|
|
@@ -21486,7 +21904,13 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21486
21904
|
const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)";
|
|
21487
21905
|
lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
|
|
21488
21906
|
lines.push(` kintone query: ${mainQ}`);
|
|
21489
|
-
|
|
21907
|
+
const mainJoinItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21908
|
+
(item) => item.targetAlias === stmt.from.alias
|
|
21909
|
+
);
|
|
21910
|
+
if (mainJoinItem) {
|
|
21911
|
+
lines.push(` pushdown applied: ${runtimeJoinPlan.queriesByAlias.get(mainJoinItem.targetAlias)}`);
|
|
21912
|
+
lines.push(` relation: ${mainJoinItem.relation}`);
|
|
21913
|
+
} else if (!runtimeJoinPlan && mainCandidate !== null) {
|
|
21490
21914
|
lines.push(` pushdown candidate: ${whereToKintone(mainCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21491
21915
|
}
|
|
21492
21916
|
lines.push(` fields: ${mainFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : mainFields.join(", ")}`);
|
|
@@ -21499,7 +21923,13 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
|
|
|
21499
21923
|
const joinQ = joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
|
|
21500
21924
|
lines.push(` ${joinType}: APP${join2.table.appId}${joinAliasStr} (${join2.table.appId})`);
|
|
21501
21925
|
lines.push(` kintone query: ${joinQ}`);
|
|
21502
|
-
|
|
21926
|
+
const joinPlanItem = runtimeJoinPlan?.joinPlan.items.find(
|
|
21927
|
+
(item) => item.targetAlias === join2.table.alias
|
|
21928
|
+
);
|
|
21929
|
+
if (joinPlanItem) {
|
|
21930
|
+
lines.push(` pushdown applied: ${runtimeJoinPlan.queriesByAlias.get(joinPlanItem.targetAlias)}`);
|
|
21931
|
+
lines.push(` relation: ${joinPlanItem.relation}`);
|
|
21932
|
+
} else if (!runtimeJoinPlan && joinCandidate !== null) {
|
|
21503
21933
|
lines.push(` pushdown candidate: ${whereToKintone(joinCandidate)}\uFF08\u5B9F\u884C\u6642\u306E\u578B\u30FB\u5B9F\u5728\u78BA\u8A8D\u5F85\u3061\uFF09`);
|
|
21504
21934
|
}
|
|
21505
21935
|
lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
|