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