@rex0220/kintone-sql-tools 3.23.0 → 3.25.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 +577 -93
- package/dist-engine/index.cjs +8 -8
- package/dist-engine/index.mjs +8 -8
- package/dist-engine/ksql-engine.umd.js +8 -8
- package/dist-engine/meta/bundle-baseline.json +10 -10
- package/dist-engine/meta/cjs.json +50 -21
- package/dist-engine/meta/esm.json +50 -21
- package/dist-engine/meta/umd.json +50 -21
- package/dist-mcp/ksql-mcp.js +583 -96
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/dist-cli/ksql.js
CHANGED
|
@@ -798,10 +798,26 @@ var RELATIVE_DATE_FUNCTION_NAMES = /* @__PURE__ */ new Set([
|
|
|
798
798
|
"LAST_YEAR",
|
|
799
799
|
"NEXT_YEAR"
|
|
800
800
|
]);
|
|
801
|
+
var LEGACY_KINTONE_FUNCTION_NAMES = /* @__PURE__ */ new Set([
|
|
802
|
+
"TODAY",
|
|
803
|
+
"NOW",
|
|
804
|
+
"LOGINUSER"
|
|
805
|
+
]);
|
|
806
|
+
var SERVER_ONLY_WHERE_FUNCTION_NAMES = /* @__PURE__ */ new Set([
|
|
807
|
+
...LEGACY_KINTONE_FUNCTION_NAMES,
|
|
808
|
+
...RELATIVE_DATE_FUNCTION_NAMES
|
|
809
|
+
]);
|
|
801
810
|
var WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN = "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN";
|
|
811
|
+
var WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN = "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN";
|
|
802
812
|
function isRelativeDateFunctionName(name) {
|
|
803
813
|
return RELATIVE_DATE_FUNCTION_NAMES.has(name);
|
|
804
814
|
}
|
|
815
|
+
function isLegacyKintoneFunctionName(name) {
|
|
816
|
+
return LEGACY_KINTONE_FUNCTION_NAMES.has(name);
|
|
817
|
+
}
|
|
818
|
+
function isServerOnlyWhereFunctionName(name) {
|
|
819
|
+
return SERVER_ONLY_WHERE_FUNCTION_NAMES.has(name);
|
|
820
|
+
}
|
|
805
821
|
|
|
806
822
|
// src/parser/parser.ts
|
|
807
823
|
var MAX_BATCH_STATEMENTS = 20;
|
|
@@ -3034,7 +3050,8 @@ var Parser = class {
|
|
|
3034
3050
|
// IN リストの値
|
|
3035
3051
|
parseInValues() {
|
|
3036
3052
|
const values = [];
|
|
3037
|
-
const invalidValueMessage = "IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u3001\u6570\u5024\u3001\
|
|
3053
|
+
const invalidValueMessage = "IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u3001\u6570\u5024\u3001\u30D0\u30C3\u30C1\u5909\u6570\u3001\u307E\u305F\u306F\u5358\u72EC\u306E LOGINUSER() \u304C\u5FC5\u8981\u3067\u3059";
|
|
3054
|
+
const mixedLoginUserMessage = "LOGINUSER() \u306F IN / NOT IN \u30EA\u30B9\u30C8\u306E\u5358\u72EC\u8981\u7D20\u3068\u3057\u3066\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059";
|
|
3038
3055
|
do {
|
|
3039
3056
|
const tok = this.advance();
|
|
3040
3057
|
if (tok.kind === "STRING" /* STRING */) {
|
|
@@ -3051,6 +3068,16 @@ var Parser = class {
|
|
|
3051
3068
|
values.push(makeNumberLiteral(`${sign}${number.value}`));
|
|
3052
3069
|
} else if (tok.kind === "VARIABLE" /* VARIABLE */) {
|
|
3053
3070
|
values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
|
|
3071
|
+
} else if (tok.kind === "LOGINUSER" /* LOGINUSER */) {
|
|
3072
|
+
if (values.length > 0) {
|
|
3073
|
+
throw new ParseError(mixedLoginUserMessage, tok);
|
|
3074
|
+
}
|
|
3075
|
+
this.expect("(" /* LPAREN */, "LOGINUSER \u306E\u76F4\u5F8C\u306B\u306F\u7A7A\u5F15\u6570\u306E () \u304C\u5FC5\u8981\u3067\u3059");
|
|
3076
|
+
this.expect(")" /* RPAREN */, "LOGINUSER \u306E\u76F4\u5F8C\u306B\u306F\u7A7A\u5F15\u6570\u306E () \u304C\u5FC5\u8981\u3067\u3059");
|
|
3077
|
+
values.push({ type: "KINTONE_FUNC", name: "LOGINUSER" });
|
|
3078
|
+
if (this.peek().kind === "," /* COMMA */) {
|
|
3079
|
+
throw new ParseError(mixedLoginUserMessage, this.peek());
|
|
3080
|
+
}
|
|
3054
3081
|
} else {
|
|
3055
3082
|
throw new ParseError(invalidValueMessage, tok);
|
|
3056
3083
|
}
|
|
@@ -5082,9 +5109,11 @@ function convertInList(v, op) {
|
|
|
5082
5109
|
throw new KintoneQueryError("IN_LIST \u306F IN / NOT IN \u6F14\u7B97\u5B50\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059");
|
|
5083
5110
|
}
|
|
5084
5111
|
assertResolvedInListValues(v.values);
|
|
5085
|
-
const values = v.values.map(
|
|
5086
|
-
(item
|
|
5087
|
-
|
|
5112
|
+
const values = v.values.map((item) => {
|
|
5113
|
+
if (item.type === "STRING") return convertString(item);
|
|
5114
|
+
if (item.type === "NUMBER") return numberLiteralText(item);
|
|
5115
|
+
return convertKintoneFunc(item);
|
|
5116
|
+
}).join(",");
|
|
5088
5117
|
return `(${values})`;
|
|
5089
5118
|
}
|
|
5090
5119
|
function assertResolvedInListValues(values) {
|
|
@@ -7682,6 +7711,12 @@ function assertResolvedInListValues2(values) {
|
|
|
7682
7711
|
if (unresolved?.type === "VARIABLE") {
|
|
7683
7712
|
throw new Error(`ParseError: unresolved batch variable @${unresolved.name}.`);
|
|
7684
7713
|
}
|
|
7714
|
+
const serverOnlyFunction = values.find((item) => item.type === "KINTONE_FUNC");
|
|
7715
|
+
if (serverOnlyFunction?.type === "KINTONE_FUNC") {
|
|
7716
|
+
throw new Error(
|
|
7717
|
+
`${serverOnlyFunction.name}: ${WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN}`
|
|
7718
|
+
);
|
|
7719
|
+
}
|
|
7685
7720
|
}
|
|
7686
7721
|
function evalNullCheck(expr, row) {
|
|
7687
7722
|
const val = resolveField(expr.field, row);
|
|
@@ -7787,11 +7822,12 @@ function resolveKintoneFuncValue(name) {
|
|
|
7787
7822
|
`${name}: ${WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN}`
|
|
7788
7823
|
);
|
|
7789
7824
|
}
|
|
7825
|
+
if (isLegacyKintoneFunctionName(name)) {
|
|
7826
|
+
throw new Error(
|
|
7827
|
+
`${name}: ${WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN}`
|
|
7828
|
+
);
|
|
7829
|
+
}
|
|
7790
7830
|
switch (name) {
|
|
7791
|
-
case "TODAY":
|
|
7792
|
-
case "NOW":
|
|
7793
|
-
case "LOGINUSER":
|
|
7794
|
-
return resolveKintoneFunc(name);
|
|
7795
7831
|
default:
|
|
7796
7832
|
throw new Error(`InternalError: unexpected KINTONE_FUNC name: ${name}`);
|
|
7797
7833
|
}
|
|
@@ -12294,6 +12330,16 @@ var RELATIVE_DATE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
12294
12330
|
"UPDATED_TIME"
|
|
12295
12331
|
]);
|
|
12296
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
|
+
]);
|
|
12297
12343
|
var EQUALITY_IN = ["=", "!=", "in", "not in"];
|
|
12298
12344
|
var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
12299
12345
|
["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
|
|
@@ -12321,6 +12367,12 @@ var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
|
|
|
12321
12367
|
["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
|
|
12322
12368
|
["STATUS", new Set(EQUALITY_IN)]
|
|
12323
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
|
+
]);
|
|
12324
12376
|
var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
12325
12377
|
"RECORD_NUMBER",
|
|
12326
12378
|
"__ID__",
|
|
@@ -12383,7 +12435,7 @@ function classifyNode(where, resolveField2) {
|
|
|
12383
12435
|
if (!hasRelativeDateReason(inner.reasons)) {
|
|
12384
12436
|
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
12385
12437
|
}
|
|
12386
|
-
return
|
|
12438
|
+
return requireExactFunctionPushdown({
|
|
12387
12439
|
capability: "LOCAL_ONLY",
|
|
12388
12440
|
reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }, ...inner.reasons]
|
|
12389
12441
|
});
|
|
@@ -12399,15 +12451,43 @@ function classifyBinary(op, left, right, resolveField2) {
|
|
|
12399
12451
|
if (right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(right.name)) {
|
|
12400
12452
|
return classifyRelativeDateBinary(op, left, right, resolveField2);
|
|
12401
12453
|
}
|
|
12454
|
+
if (right.type === "KINTONE_FUNC" && isLegacyKintoneFunction(right)) {
|
|
12455
|
+
return classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2);
|
|
12456
|
+
}
|
|
12457
|
+
if (right.type === "IN_LIST") {
|
|
12458
|
+
const functions = right.values.filter(
|
|
12459
|
+
(value) => value.type === "KINTONE_FUNC"
|
|
12460
|
+
);
|
|
12461
|
+
if (functions.length > 0) {
|
|
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
|
+
);
|
|
12472
|
+
}
|
|
12473
|
+
}
|
|
12402
12474
|
if (left.type !== "FIELD") return localExpression();
|
|
12403
12475
|
const semantics = resolveField2(left);
|
|
12404
12476
|
if (!semantics) {
|
|
12405
12477
|
return unsupported2("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
|
|
12406
12478
|
}
|
|
12479
|
+
const nativeOp = normalizeOperator(op);
|
|
12480
|
+
if (!isLocallyValidOperator(semantics.fieldType, nativeOp)) {
|
|
12481
|
+
return unsupported2(
|
|
12482
|
+
"WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE",
|
|
12483
|
+
left.field,
|
|
12484
|
+
semantics.fieldType,
|
|
12485
|
+
nativeOp
|
|
12486
|
+
);
|
|
12487
|
+
}
|
|
12407
12488
|
if (!hasLocalContract(semantics.fieldType, op)) {
|
|
12408
|
-
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType,
|
|
12489
|
+
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, nativeOp);
|
|
12409
12490
|
}
|
|
12410
|
-
const nativeOp = normalizeOperator(op);
|
|
12411
12491
|
const native = nativeWhereOperatorsForType(semantics.fieldType);
|
|
12412
12492
|
const rightCanPush = right.type === "STRING" || right.type === "NUMBER" || right.type === "IN_LIST" || isLegacyKintoneFunction(right);
|
|
12413
12493
|
const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
|
|
@@ -12434,7 +12514,51 @@ function classifyBinary(op, left, right, resolveField2) {
|
|
|
12434
12514
|
};
|
|
12435
12515
|
}
|
|
12436
12516
|
function isLegacyKintoneFunction(value) {
|
|
12437
|
-
return value.type === "KINTONE_FUNC" && (value.name
|
|
12517
|
+
return value.type === "KINTONE_FUNC" && LEGACY_KINTONE_FUNCTION_NAMES.has(value.name);
|
|
12518
|
+
}
|
|
12519
|
+
function classifyLegacyKintoneFunctionBinary(op, left, right, resolveField2) {
|
|
12520
|
+
const operator = normalizeOperator(op);
|
|
12521
|
+
const functionName = right.name;
|
|
12522
|
+
if (!LEGACY_KINTONE_FUNCTION_NAMES.has(functionName) || left.type !== "FIELD") {
|
|
12523
|
+
return legacyKintoneFunctionUnsupported(
|
|
12524
|
+
"WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED",
|
|
12525
|
+
functionName,
|
|
12526
|
+
void 0,
|
|
12527
|
+
void 0,
|
|
12528
|
+
operator
|
|
12529
|
+
);
|
|
12530
|
+
}
|
|
12531
|
+
const semantics = resolveField2(left);
|
|
12532
|
+
const validFieldTypes = LEGACY_KINTONE_FUNCTION_FIELD_TYPES.get(functionName);
|
|
12533
|
+
if (!semantics || !validFieldTypes.has(semantics.fieldType) || semantics.inSubtable || semantics.requiresCollectionOperators) {
|
|
12534
|
+
return legacyKintoneFunctionUnsupported(
|
|
12535
|
+
"WHERE_KINTONE_FUNCTION_FIELD_TYPE_UNSUPPORTED",
|
|
12536
|
+
functionName,
|
|
12537
|
+
left.field,
|
|
12538
|
+
semantics?.fieldType,
|
|
12539
|
+
operator
|
|
12540
|
+
);
|
|
12541
|
+
}
|
|
12542
|
+
const validOperators = LEGACY_KINTONE_FUNCTION_OPERATORS.get(functionName);
|
|
12543
|
+
if (!validOperators.has(operator)) {
|
|
12544
|
+
return legacyKintoneFunctionUnsupported(
|
|
12545
|
+
"WHERE_KINTONE_FUNCTION_OPERATOR_UNSUPPORTED",
|
|
12546
|
+
functionName,
|
|
12547
|
+
left.field,
|
|
12548
|
+
semantics.fieldType,
|
|
12549
|
+
operator
|
|
12550
|
+
);
|
|
12551
|
+
}
|
|
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
|
+
};
|
|
12438
12562
|
}
|
|
12439
12563
|
function classifyRelativeDateBinary(op, left, right, resolveField2) {
|
|
12440
12564
|
const operator = normalizeOperator(op);
|
|
@@ -12522,6 +12646,14 @@ function hasValidRelativeDateArguments(value) {
|
|
|
12522
12646
|
function classifyLocalOnlyField(field, operator, resolveField2) {
|
|
12523
12647
|
const semantics = resolveField2(field);
|
|
12524
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
|
+
}
|
|
12525
12657
|
if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
|
|
12526
12658
|
return unsupported2("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
|
|
12527
12659
|
}
|
|
@@ -12530,6 +12662,10 @@ function classifyLocalOnlyField(field, operator, resolveField2) {
|
|
|
12530
12662
|
reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
|
|
12531
12663
|
};
|
|
12532
12664
|
}
|
|
12665
|
+
function isLocallyValidOperator(fieldType, operator) {
|
|
12666
|
+
const policy = LOCAL_VALID_OPERATORS.get(fieldType);
|
|
12667
|
+
return policy === void 0 || policy.has(operator);
|
|
12668
|
+
}
|
|
12533
12669
|
function hasLocalContract(fieldType, op) {
|
|
12534
12670
|
if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
|
|
12535
12671
|
if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
|
|
@@ -12556,18 +12692,18 @@ function normalizeOperator(op) {
|
|
|
12556
12692
|
function combineLogical(op, left, right) {
|
|
12557
12693
|
const reasons = [...left.reasons, ...right.reasons];
|
|
12558
12694
|
if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
|
|
12559
|
-
return
|
|
12695
|
+
return requireExactFunctionPushdown({ capability: "UNSUPPORTED", reasons });
|
|
12560
12696
|
}
|
|
12561
12697
|
if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
|
|
12562
12698
|
return { capability: "EXACT_PUSHDOWN", reasons };
|
|
12563
12699
|
}
|
|
12564
12700
|
if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
|
|
12565
|
-
return
|
|
12701
|
+
return requireExactFunctionPushdown({
|
|
12566
12702
|
capability: "SUPERSET_PREFILTER",
|
|
12567
12703
|
reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
|
|
12568
12704
|
});
|
|
12569
12705
|
}
|
|
12570
|
-
return
|
|
12706
|
+
return requireExactFunctionPushdown({ capability: "LOCAL_ONLY", reasons });
|
|
12571
12707
|
}
|
|
12572
12708
|
function localExpression() {
|
|
12573
12709
|
return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
|
|
@@ -12575,6 +12711,12 @@ function localExpression() {
|
|
|
12575
12711
|
function unsupported2(code, field, fieldType, operator) {
|
|
12576
12712
|
return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
|
|
12577
12713
|
}
|
|
12714
|
+
function legacyKintoneFunctionUnsupported(code, functionName, field, fieldType, operator) {
|
|
12715
|
+
return requireExactFunctionPushdown({
|
|
12716
|
+
capability: "UNSUPPORTED",
|
|
12717
|
+
reasons: [{ code, functionName, field, fieldType, operator }]
|
|
12718
|
+
});
|
|
12719
|
+
}
|
|
12578
12720
|
function relativeDateUnsupported(code, functionName, field, fieldType, operator) {
|
|
12579
12721
|
return requireExactRelativeDatePushdown({
|
|
12580
12722
|
capability: "UNSUPPORTED",
|
|
@@ -12582,13 +12724,22 @@ function relativeDateUnsupported(code, functionName, field, fieldType, operator)
|
|
|
12582
12724
|
});
|
|
12583
12725
|
}
|
|
12584
12726
|
function hasRelativeDateReason(reasons) {
|
|
12585
|
-
return reasons.some(
|
|
12727
|
+
return reasons.some(
|
|
12728
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
12729
|
+
);
|
|
12730
|
+
}
|
|
12731
|
+
function hasLegacyKintoneFunctionReason(reasons) {
|
|
12732
|
+
return reasons.some(
|
|
12733
|
+
(reason) => reason.code.startsWith("WHERE_KINTONE_FUNCTION_") || reason.functionName !== void 0 && LEGACY_KINTONE_FUNCTION_NAMES.has(reason.functionName)
|
|
12734
|
+
);
|
|
12586
12735
|
}
|
|
12587
12736
|
function requireExactRelativeDatePushdown(result) {
|
|
12588
12737
|
if (result.capability === "EXACT_PUSHDOWN" || !hasRelativeDateReason(result.reasons) || result.reasons.some((reason) => reason.code === "WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN")) {
|
|
12589
12738
|
return result;
|
|
12590
12739
|
}
|
|
12591
|
-
const relative = result.reasons.find(
|
|
12740
|
+
const relative = result.reasons.find(
|
|
12741
|
+
(reason) => reason.code.startsWith("WHERE_RELATIVE_DATE_") || reason.functionName !== void 0 && isRelativeDateFunctionName(reason.functionName)
|
|
12742
|
+
);
|
|
12592
12743
|
return {
|
|
12593
12744
|
capability: result.capability,
|
|
12594
12745
|
reasons: [
|
|
@@ -12603,6 +12754,161 @@ function requireExactRelativeDatePushdown(result) {
|
|
|
12603
12754
|
]
|
|
12604
12755
|
};
|
|
12605
12756
|
}
|
|
12757
|
+
function requireExactLegacyKintoneFunctionPushdown(result) {
|
|
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
|
+
);
|
|
12766
|
+
return {
|
|
12767
|
+
capability: result.capability,
|
|
12768
|
+
reasons: [
|
|
12769
|
+
...result.reasons,
|
|
12770
|
+
{
|
|
12771
|
+
code: "WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN",
|
|
12772
|
+
functionName: legacy.functionName,
|
|
12773
|
+
field: legacy.field,
|
|
12774
|
+
fieldType: legacy.fieldType,
|
|
12775
|
+
operator: legacy.operator
|
|
12776
|
+
}
|
|
12777
|
+
]
|
|
12778
|
+
};
|
|
12779
|
+
}
|
|
12780
|
+
function requireExactFunctionPushdown(result) {
|
|
12781
|
+
return requireExactLegacyKintoneFunctionPushdown(
|
|
12782
|
+
requireExactRelativeDatePushdown(result)
|
|
12783
|
+
);
|
|
12784
|
+
}
|
|
12785
|
+
|
|
12786
|
+
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
12787
|
+
function buildRelativeDateFullScanExactPlan(input) {
|
|
12788
|
+
const {
|
|
12789
|
+
select,
|
|
12790
|
+
selectMode,
|
|
12791
|
+
capability,
|
|
12792
|
+
context,
|
|
12793
|
+
serializedWholeWhere,
|
|
12794
|
+
relativeFunctionNames
|
|
12795
|
+
} = input;
|
|
12796
|
+
if (select.where === null) return null;
|
|
12797
|
+
if (select.from.appId <= 0 || select.from.cteName !== null) return null;
|
|
12798
|
+
if (select.from.subtableCode) return null;
|
|
12799
|
+
if (select.joins.length > 0) return null;
|
|
12800
|
+
if (!context.allowFullScanExact) return null;
|
|
12801
|
+
if (select.orderMode === "KINTONE_NATIVE") return null;
|
|
12802
|
+
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
12803
|
+
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
12804
|
+
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
12805
|
+
const occurrences = serverOnlyFunctionOccurrencesInWhere(select.where);
|
|
12806
|
+
if (occurrences.length === 0) return null;
|
|
12807
|
+
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
12808
|
+
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
12809
|
+
return null;
|
|
12810
|
+
}
|
|
12811
|
+
const prefilterPlan = {
|
|
12812
|
+
prefilterWhere: select.where,
|
|
12813
|
+
residualWhere: null,
|
|
12814
|
+
exactRelativeLeaves: collectExactServerFunctionLeaves(select.where),
|
|
12815
|
+
relativeFunctionNames: new Set(occurrences),
|
|
12816
|
+
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
12817
|
+
capability: capability.capability,
|
|
12818
|
+
reasons: capability.reasons
|
|
12819
|
+
};
|
|
12820
|
+
const plan = {
|
|
12821
|
+
allowForm: "FULL_SCAN_EXACT",
|
|
12822
|
+
clientWhereEvaluation: false,
|
|
12823
|
+
serializedWholeWhere,
|
|
12824
|
+
prefilterPlan
|
|
12825
|
+
};
|
|
12826
|
+
assertRelativeDateFullScanExactPlan(plan, input, occurrences);
|
|
12827
|
+
return plan;
|
|
12828
|
+
}
|
|
12829
|
+
function assertRelativeDateFullScanExactPlan(plan, input, occurrences) {
|
|
12830
|
+
if (plan.allowForm !== "FULL_SCAN_EXACT") {
|
|
12831
|
+
throw new Error("FULL_SCAN_EXACT invariant: allowForm");
|
|
12832
|
+
}
|
|
12833
|
+
if (plan.clientWhereEvaluation !== false) {
|
|
12834
|
+
throw new Error("FULL_SCAN_EXACT invariant: clientWhereEvaluation");
|
|
12835
|
+
}
|
|
12836
|
+
if (input.capability.capability !== "EXACT_PUSHDOWN") {
|
|
12837
|
+
throw new Error("FULL_SCAN_EXACT invariant: capability");
|
|
12838
|
+
}
|
|
12839
|
+
if (input.select.where === null || plan.prefilterPlan.prefilterWhere !== input.select.where) {
|
|
12840
|
+
throw new Error("FULL_SCAN_EXACT invariant: whole WHERE identity");
|
|
12841
|
+
}
|
|
12842
|
+
if (plan.prefilterPlan.residualWhere !== null) {
|
|
12843
|
+
throw new Error("FULL_SCAN_EXACT invariant: residualWhere");
|
|
12844
|
+
}
|
|
12845
|
+
if (plan.prefilterPlan.capability !== "EXACT_PUSHDOWN") {
|
|
12846
|
+
throw new Error("FULL_SCAN_EXACT invariant: transport capability");
|
|
12847
|
+
}
|
|
12848
|
+
if (!serializedMultisetContains(plan.serializedWholeWhere, occurrences)) {
|
|
12849
|
+
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
12850
|
+
}
|
|
12851
|
+
}
|
|
12852
|
+
function serverOnlyFunctionOccurrencesInWhere(where) {
|
|
12853
|
+
const names = [];
|
|
12854
|
+
const visit = (node) => {
|
|
12855
|
+
if (Array.isArray(node)) {
|
|
12856
|
+
node.forEach(visit);
|
|
12857
|
+
return;
|
|
12858
|
+
}
|
|
12859
|
+
if (node === null || typeof node !== "object") return;
|
|
12860
|
+
const value = node;
|
|
12861
|
+
if (value["type"] === "SELECT") return;
|
|
12862
|
+
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isServerOnlyWhereFunctionName(value["name"])) {
|
|
12863
|
+
names.push(value["name"]);
|
|
12864
|
+
return;
|
|
12865
|
+
}
|
|
12866
|
+
Object.values(value).forEach(visit);
|
|
12867
|
+
};
|
|
12868
|
+
visit(where);
|
|
12869
|
+
return names;
|
|
12870
|
+
}
|
|
12871
|
+
function collectExactServerFunctionLeaves(where) {
|
|
12872
|
+
const leaves = [];
|
|
12873
|
+
const visit = (node) => {
|
|
12874
|
+
switch (node.type) {
|
|
12875
|
+
case "BINARY":
|
|
12876
|
+
if (node.right.type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.name) || node.right.type === "IN_LIST" && node.right.values.length === 1 && node.right.values[0].type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(node.right.values[0].name)) {
|
|
12877
|
+
leaves.push(node);
|
|
12878
|
+
}
|
|
12879
|
+
return;
|
|
12880
|
+
case "LOGICAL":
|
|
12881
|
+
visit(node.left);
|
|
12882
|
+
visit(node.right);
|
|
12883
|
+
return;
|
|
12884
|
+
case "NOT":
|
|
12885
|
+
case "GROUP":
|
|
12886
|
+
visit(node.expr);
|
|
12887
|
+
return;
|
|
12888
|
+
case "EXISTS":
|
|
12889
|
+
case "NULL_CHECK":
|
|
12890
|
+
case "BOOLEAN":
|
|
12891
|
+
return;
|
|
12892
|
+
}
|
|
12893
|
+
};
|
|
12894
|
+
visit(where);
|
|
12895
|
+
return leaves;
|
|
12896
|
+
}
|
|
12897
|
+
function sameOccurrenceList(actual, expected) {
|
|
12898
|
+
return actual.length === expected.length && actual.every((name, index) => name === expected[index]);
|
|
12899
|
+
}
|
|
12900
|
+
function serializedMultisetContains(query, expectedNames) {
|
|
12901
|
+
const expected = /* @__PURE__ */ new Map();
|
|
12902
|
+
for (const name of expectedNames) {
|
|
12903
|
+
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
12904
|
+
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
12905
|
+
}
|
|
12906
|
+
for (const [name, count] of expected) {
|
|
12907
|
+
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
12908
|
+
if ((matches?.length ?? 0) < count) return false;
|
|
12909
|
+
}
|
|
12910
|
+
return true;
|
|
12911
|
+
}
|
|
12606
12912
|
|
|
12607
12913
|
// src/core/optimization/relativeDatePushdownGuard.ts
|
|
12608
12914
|
function relativeDateFunctionNamesInNode(node, stopAtNestedSelect) {
|
|
@@ -12615,7 +12921,7 @@ function relativeDateFunctionNamesInNode(node, stopAtNestedSelect) {
|
|
|
12615
12921
|
}
|
|
12616
12922
|
if (valueNode === null || typeof valueNode !== "object") return;
|
|
12617
12923
|
const value = valueNode;
|
|
12618
|
-
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" &&
|
|
12924
|
+
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isServerOnlyWhereFunctionName(value["name"]) && !seen.has(value["name"])) {
|
|
12619
12925
|
seen.add(value["name"]);
|
|
12620
12926
|
names.push(value["name"]);
|
|
12621
12927
|
return;
|
|
@@ -12649,7 +12955,7 @@ function nestedSelects(node, root) {
|
|
|
12649
12955
|
visit(node);
|
|
12650
12956
|
return found;
|
|
12651
12957
|
}
|
|
12652
|
-
function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = true) {
|
|
12958
|
+
function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = true, allowFullScanExact = true, forceNestedForbidden = forceForbidden) {
|
|
12653
12959
|
const functionNames = relativeDateFunctionNamesInWhere(select.where);
|
|
12654
12960
|
if (functionNames.length > 0) {
|
|
12655
12961
|
candidates.push({
|
|
@@ -12658,7 +12964,9 @@ function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = t
|
|
|
12658
12964
|
where: select.where,
|
|
12659
12965
|
functionNames,
|
|
12660
12966
|
path,
|
|
12661
|
-
allowPhase2Prefilter: allowPhase2
|
|
12967
|
+
allowPhase2Prefilter: allowPhase2,
|
|
12968
|
+
allowFullScanExact,
|
|
12969
|
+
relativeFunctionOccurrences: serverOnlyFunctionOccurrencesInWhere(select.where)
|
|
12662
12970
|
});
|
|
12663
12971
|
}
|
|
12664
12972
|
nestedSelects(select, select).forEach(
|
|
@@ -12666,32 +12974,53 @@ function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = t
|
|
|
12666
12974
|
nested,
|
|
12667
12975
|
`${path}.select-source[${index}]`,
|
|
12668
12976
|
candidates,
|
|
12669
|
-
|
|
12670
|
-
allowPhase2
|
|
12977
|
+
forceNestedForbidden,
|
|
12978
|
+
forceNestedForbidden ? false : allowPhase2,
|
|
12979
|
+
forceNestedForbidden ? false : allowFullScanExact,
|
|
12980
|
+
forceNestedForbidden
|
|
12671
12981
|
)
|
|
12672
12982
|
);
|
|
12673
12983
|
}
|
|
12674
|
-
function collectUnion(union, path, candidates, forceForbidden) {
|
|
12675
|
-
if (union.left.type === "UNION")
|
|
12676
|
-
|
|
12677
|
-
|
|
12984
|
+
function collectUnion(union, path, candidates, forceForbidden, allowFullScanExact = true) {
|
|
12985
|
+
if (union.left.type === "UNION") {
|
|
12986
|
+
collectUnion(union.left, `${path}.left`, candidates, forceForbidden, allowFullScanExact);
|
|
12987
|
+
} else {
|
|
12988
|
+
collectSelect(union.left, `${path}.left`, candidates, forceForbidden, true, allowFullScanExact);
|
|
12989
|
+
}
|
|
12990
|
+
collectSelect(union.right, `${path}.right`, candidates, forceForbidden, true, allowFullScanExact);
|
|
12678
12991
|
}
|
|
12679
12992
|
function collectWith(statement, path, candidates, inheritedForbidden) {
|
|
12680
12993
|
if (!inheritedForbidden && canInlineSingleCte(statement)) {
|
|
12681
|
-
collectSelect(buildInlinedQuery(statement), `${path}.inlined`, candidates, false);
|
|
12994
|
+
collectSelect(buildInlinedQuery(statement), `${path}.inlined`, candidates, false, true, true);
|
|
12682
12995
|
return;
|
|
12683
12996
|
}
|
|
12684
12997
|
statement.ctes.forEach((cte, index) => {
|
|
12685
12998
|
if (cte.query.type === "SELECT") {
|
|
12686
|
-
collectSelect(
|
|
12999
|
+
collectSelect(
|
|
13000
|
+
cte.query,
|
|
13001
|
+
`${path}.cte[${index}]`,
|
|
13002
|
+
candidates,
|
|
13003
|
+
inheritedForbidden,
|
|
13004
|
+
false,
|
|
13005
|
+
!inheritedForbidden,
|
|
13006
|
+
true
|
|
13007
|
+
);
|
|
12687
13008
|
} else if (cte.query.type === "UNION") {
|
|
12688
|
-
collectUnion(cte.query, `${path}.cte[${index}]`, candidates, true);
|
|
13009
|
+
collectUnion(cte.query, `${path}.cte[${index}]`, candidates, true, false);
|
|
12689
13010
|
}
|
|
12690
13011
|
});
|
|
12691
13012
|
if (statement.query.type === "SELECT") {
|
|
12692
|
-
collectSelect(
|
|
13013
|
+
collectSelect(
|
|
13014
|
+
statement.query,
|
|
13015
|
+
`${path}.main`,
|
|
13016
|
+
candidates,
|
|
13017
|
+
inheritedForbidden,
|
|
13018
|
+
false,
|
|
13019
|
+
!inheritedForbidden,
|
|
13020
|
+
true
|
|
13021
|
+
);
|
|
12693
13022
|
} else {
|
|
12694
|
-
collectUnion(statement.query, `${path}.main`, candidates, true);
|
|
13023
|
+
collectUnion(statement.query, `${path}.main`, candidates, true, false);
|
|
12695
13024
|
}
|
|
12696
13025
|
}
|
|
12697
13026
|
function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
@@ -12706,9 +13035,9 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12706
13035
|
collectWith(statement, path, candidates, forceForbidden);
|
|
12707
13036
|
return;
|
|
12708
13037
|
case "CREATE_TEMP_TABLE":
|
|
12709
|
-
if (statement.query.type === "WITH") collectWith(statement.query, `${path}.query`, candidates,
|
|
12710
|
-
else if (statement.query.type === "UNION") collectUnion(statement.query, `${path}.query`, candidates, true);
|
|
12711
|
-
else collectSelect(statement.query, `${path}.query`, candidates, true);
|
|
13038
|
+
if (statement.query.type === "WITH") collectWith(statement.query, `${path}.query`, candidates, false);
|
|
13039
|
+
else if (statement.query.type === "UNION") collectUnion(statement.query, `${path}.query`, candidates, true, false);
|
|
13040
|
+
else collectSelect(statement.query, `${path}.query`, candidates, false, false, true, true);
|
|
12712
13041
|
return;
|
|
12713
13042
|
case "EXPLAIN":
|
|
12714
13043
|
collectStatement(statement.query, `${path}.query`, candidates, forceForbidden);
|
|
@@ -12722,11 +13051,13 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12722
13051
|
source: statement,
|
|
12723
13052
|
where: statement.where,
|
|
12724
13053
|
functionNames,
|
|
12725
|
-
path
|
|
13054
|
+
path,
|
|
13055
|
+
allowFullScanExact: false,
|
|
13056
|
+
relativeFunctionOccurrences: serverOnlyFunctionOccurrencesInWhere(statement.where)
|
|
12726
13057
|
});
|
|
12727
13058
|
}
|
|
12728
13059
|
nestedSelects(statement, statement).forEach(
|
|
12729
|
-
(select, index) => collectSelect(select, `${path}.select-source[${index}]`, candidates, true)
|
|
13060
|
+
(select, index) => collectSelect(select, `${path}.select-source[${index}]`, candidates, true, true, false)
|
|
12730
13061
|
);
|
|
12731
13062
|
return;
|
|
12732
13063
|
}
|
|
@@ -12740,7 +13071,9 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12740
13071
|
source: statement,
|
|
12741
13072
|
where: statement.where,
|
|
12742
13073
|
functionNames,
|
|
12743
|
-
path
|
|
13074
|
+
path,
|
|
13075
|
+
allowFullScanExact: false,
|
|
13076
|
+
relativeFunctionOccurrences: serverOnlyFunctionOccurrencesInWhere(statement.where)
|
|
12744
13077
|
});
|
|
12745
13078
|
}
|
|
12746
13079
|
if (statement.type === "UPDATE" && statement.applyBlocks?.length) {
|
|
@@ -12751,7 +13084,9 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12751
13084
|
source: statement,
|
|
12752
13085
|
where: null,
|
|
12753
13086
|
functionNames: applyFunctions,
|
|
12754
|
-
path: `${path}.apply
|
|
13087
|
+
path: `${path}.apply`,
|
|
13088
|
+
allowFullScanExact: false,
|
|
13089
|
+
relativeFunctionOccurrences: []
|
|
12755
13090
|
});
|
|
12756
13091
|
}
|
|
12757
13092
|
}
|
|
@@ -12761,6 +13096,7 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12761
13096
|
`${path}.select-source[${index}]`,
|
|
12762
13097
|
candidates,
|
|
12763
13098
|
forceForbidden,
|
|
13099
|
+
false,
|
|
12764
13100
|
false
|
|
12765
13101
|
)
|
|
12766
13102
|
);
|
|
@@ -12773,13 +13109,23 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12773
13109
|
`${path}.select-source[${index}]`,
|
|
12774
13110
|
candidates,
|
|
12775
13111
|
forceForbidden,
|
|
13112
|
+
false,
|
|
12776
13113
|
false
|
|
12777
13114
|
)
|
|
12778
13115
|
);
|
|
12779
13116
|
}
|
|
12780
13117
|
}
|
|
12781
13118
|
function serializationContainsFunctions(query, names) {
|
|
12782
|
-
|
|
13119
|
+
const expected = /* @__PURE__ */ new Map();
|
|
13120
|
+
for (const name of names) {
|
|
13121
|
+
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
13122
|
+
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
13123
|
+
}
|
|
13124
|
+
for (const [name, count] of expected) {
|
|
13125
|
+
const matches = query.match(new RegExp(`\\b${name}\\s*\\(`, "g"));
|
|
13126
|
+
if ((matches?.length ?? 0) < count) return false;
|
|
13127
|
+
}
|
|
13128
|
+
return true;
|
|
12783
13129
|
}
|
|
12784
13130
|
function allowRelativeDatePrefilterPlan(select, decomposition) {
|
|
12785
13131
|
return decomposition.eligible === true && resolveSelectMode(select) === "FULL_SCAN" && select.orderMode !== "KINTONE_NATIVE" && select.from.cteName === null && !select.from.subtableCode && select.joins.length === 0;
|
|
@@ -12794,23 +13140,36 @@ function rejectedNode(candidate) {
|
|
|
12794
13140
|
allowed: false
|
|
12795
13141
|
};
|
|
12796
13142
|
}
|
|
12797
|
-
function
|
|
13143
|
+
function serverFunctionReasonCodes(functionName, capability) {
|
|
12798
13144
|
const codes = (capability?.reasons ?? []).filter(
|
|
12799
|
-
(reason) => reason.functionName
|
|
12800
|
-
).map((reason) => reason.code).filter(
|
|
12801
|
-
|
|
13145
|
+
(reason) => reason.functionName === functionName
|
|
13146
|
+
).map((reason) => reason.code).filter(
|
|
13147
|
+
(code) => code.startsWith("WHERE_RELATIVE_DATE_") || code.startsWith("WHERE_KINTONE_FUNCTION_")
|
|
13148
|
+
);
|
|
13149
|
+
if (isLegacyKintoneFunctionName(functionName)) {
|
|
13150
|
+
if (!codes.some(
|
|
13151
|
+
(code) => code !== WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN
|
|
13152
|
+
)) {
|
|
13153
|
+
codes.unshift("WHERE_KINTONE_FUNCTION_CONTEXT_UNSUPPORTED");
|
|
13154
|
+
}
|
|
13155
|
+
if (!codes.includes(WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN)) {
|
|
13156
|
+
codes.push(WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN);
|
|
13157
|
+
}
|
|
13158
|
+
} else if (!codes.includes(WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN)) {
|
|
12802
13159
|
codes.push(WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN);
|
|
12803
13160
|
}
|
|
12804
13161
|
return [...new Set(codes)];
|
|
12805
13162
|
}
|
|
12806
13163
|
function rejectionFor(candidate, capability) {
|
|
12807
|
-
const
|
|
13164
|
+
const functionName = candidate.functionNames[0];
|
|
13165
|
+
const requiresExact = isLegacyKintoneFunctionName(functionName) ? WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN : WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN;
|
|
13166
|
+
const reasonCodes = serverFunctionReasonCodes(functionName, capability);
|
|
12808
13167
|
return {
|
|
12809
|
-
functionName
|
|
13168
|
+
functionName,
|
|
12810
13169
|
path: candidate.path,
|
|
12811
13170
|
code: reasonCodes.find(
|
|
12812
|
-
(code) => code !==
|
|
12813
|
-
) ??
|
|
13171
|
+
(code) => code !== requiresExact
|
|
13172
|
+
) ?? requiresExact,
|
|
12814
13173
|
reasonCodes
|
|
12815
13174
|
};
|
|
12816
13175
|
}
|
|
@@ -12823,7 +13182,8 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12823
13182
|
const node2 = rejectedNode(candidate);
|
|
12824
13183
|
nodes.push(node2);
|
|
12825
13184
|
return {
|
|
12826
|
-
hasRelativeDate:
|
|
13185
|
+
hasRelativeDate: candidate.functionNames.some(isRelativeDateFunctionName),
|
|
13186
|
+
hasServerOnlyWhereFunction: true,
|
|
12827
13187
|
nodes,
|
|
12828
13188
|
allowed: false,
|
|
12829
13189
|
rejection: rejectionFor(candidate)
|
|
@@ -12840,7 +13200,10 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12840
13200
|
} catch {
|
|
12841
13201
|
restQuery2 = "";
|
|
12842
13202
|
}
|
|
12843
|
-
let allowed2 = physicalTopLevel && selectMode === "SIMPLE" && (select.orderBy.length === 0 || select.orderMode === "KINTONE_NATIVE") && capability2.capability === "EXACT_PUSHDOWN" && serializationContainsFunctions(
|
|
13203
|
+
let allowed2 = physicalTopLevel && selectMode === "SIMPLE" && (select.orderBy.length === 0 || select.orderMode === "KINTONE_NATIVE") && capability2.capability === "EXACT_PUSHDOWN" && serializationContainsFunctions(
|
|
13204
|
+
restQuery2,
|
|
13205
|
+
candidate.relativeFunctionOccurrences
|
|
13206
|
+
);
|
|
12844
13207
|
let prefilterPlan;
|
|
12845
13208
|
let phase2PrefilterEligible;
|
|
12846
13209
|
if (!allowed2 && candidate.allowPhase2Prefilter !== false && capability2.capability === "SUPERSET_PREFILTER" && resolver.prefilterDecomposition) {
|
|
@@ -12851,6 +13214,21 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12851
13214
|
allowed2 = true;
|
|
12852
13215
|
}
|
|
12853
13216
|
}
|
|
13217
|
+
let fullScanExactPlan;
|
|
13218
|
+
if (!allowed2 && capability2.capability === "EXACT_PUSHDOWN") {
|
|
13219
|
+
fullScanExactPlan = buildRelativeDateFullScanExactPlan({
|
|
13220
|
+
select,
|
|
13221
|
+
selectMode,
|
|
13222
|
+
capability: capability2,
|
|
13223
|
+
context: { allowFullScanExact: candidate.allowFullScanExact },
|
|
13224
|
+
serializedWholeWhere: restQuery2 || null,
|
|
13225
|
+
relativeFunctionNames: candidate.relativeFunctionOccurrences
|
|
13226
|
+
}) ?? void 0;
|
|
13227
|
+
if (fullScanExactPlan) {
|
|
13228
|
+
prefilterPlan = fullScanExactPlan.prefilterPlan;
|
|
13229
|
+
allowed2 = true;
|
|
13230
|
+
}
|
|
13231
|
+
}
|
|
12854
13232
|
const node2 = {
|
|
12855
13233
|
kind: candidate.kind,
|
|
12856
13234
|
source: candidate.source,
|
|
@@ -12860,13 +13238,17 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12860
13238
|
capability: capability2,
|
|
12861
13239
|
restQuery: restQuery2,
|
|
12862
13240
|
...prefilterPlan ? { prefilterPlan, phase2PrefilterEligible } : {},
|
|
13241
|
+
...fullScanExactPlan ? { fullScanExactPlan, allowForm: fullScanExactPlan.allowForm } : {},
|
|
12863
13242
|
clientWhereEvaluation: !allowed2,
|
|
12864
13243
|
allowed: allowed2
|
|
12865
13244
|
};
|
|
12866
13245
|
nodes.push(node2);
|
|
12867
13246
|
if (!allowed2) {
|
|
12868
13247
|
return {
|
|
12869
|
-
hasRelativeDate:
|
|
13248
|
+
hasRelativeDate: candidates.some(
|
|
13249
|
+
(entry) => entry.functionNames.some(isRelativeDateFunctionName)
|
|
13250
|
+
),
|
|
13251
|
+
hasServerOnlyWhereFunction: true,
|
|
12870
13252
|
nodes,
|
|
12871
13253
|
allowed: false,
|
|
12872
13254
|
rejection: rejectionFor(candidate, capability2)
|
|
@@ -12882,7 +13264,10 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12882
13264
|
} catch {
|
|
12883
13265
|
restQuery = "";
|
|
12884
13266
|
}
|
|
12885
|
-
const allowed = capability.capability === "EXACT_PUSHDOWN" && serializationContainsFunctions(
|
|
13267
|
+
const allowed = capability.capability === "EXACT_PUSHDOWN" && serializationContainsFunctions(
|
|
13268
|
+
restQuery,
|
|
13269
|
+
candidate.relativeFunctionOccurrences
|
|
13270
|
+
);
|
|
12886
13271
|
const node = {
|
|
12887
13272
|
kind: candidate.kind,
|
|
12888
13273
|
source: candidate.source,
|
|
@@ -12896,7 +13281,10 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12896
13281
|
nodes.push(node);
|
|
12897
13282
|
if (!allowed) {
|
|
12898
13283
|
return {
|
|
12899
|
-
hasRelativeDate:
|
|
13284
|
+
hasRelativeDate: candidates.some(
|
|
13285
|
+
(entry) => entry.functionNames.some(isRelativeDateFunctionName)
|
|
13286
|
+
),
|
|
13287
|
+
hasServerOnlyWhereFunction: true,
|
|
12900
13288
|
nodes,
|
|
12901
13289
|
allowed: false,
|
|
12902
13290
|
rejection: rejectionFor(candidate, capability)
|
|
@@ -12904,18 +13292,22 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12904
13292
|
}
|
|
12905
13293
|
}
|
|
12906
13294
|
return {
|
|
12907
|
-
hasRelativeDate: candidates.
|
|
13295
|
+
hasRelativeDate: candidates.some(
|
|
13296
|
+
(entry) => entry.functionNames.some(isRelativeDateFunctionName)
|
|
13297
|
+
),
|
|
13298
|
+
hasServerOnlyWhereFunction: candidates.length > 0,
|
|
12908
13299
|
nodes,
|
|
12909
13300
|
allowed: true
|
|
12910
13301
|
};
|
|
12911
13302
|
}
|
|
12912
13303
|
function assertRelativeDatePushdownPlan(plan) {
|
|
12913
13304
|
if (!plan.allowed && plan.rejection) {
|
|
13305
|
+
const requiresExact = isLegacyKintoneFunctionName(plan.rejection.functionName) ? WHERE_KINTONE_FUNCTION_REQUIRES_EXACT_PUSHDOWN : WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN;
|
|
12914
13306
|
const details = plan.rejection.reasonCodes.filter(
|
|
12915
|
-
(code) => code !==
|
|
13307
|
+
(code) => code !== requiresExact
|
|
12916
13308
|
);
|
|
12917
13309
|
throw new Error(
|
|
12918
|
-
`${plan.rejection.functionName}: ${
|
|
13310
|
+
`${plan.rejection.functionName}: ${requiresExact}${details.length > 0 ? ` (reason=${details.join(", ")})` : ""} (path=${plan.rejection.path})`
|
|
12919
13311
|
);
|
|
12920
13312
|
}
|
|
12921
13313
|
}
|
|
@@ -12936,9 +13328,9 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
12936
13328
|
if (stmt.from.cteName !== null || stmt.from.appId <= 0) {
|
|
12937
13329
|
return reject(["NOT_DIRECT_PHYSICAL_APP"]);
|
|
12938
13330
|
}
|
|
12939
|
-
const occurrences =
|
|
13331
|
+
const occurrences = collectServerFunctionOccurrences(stmt.where);
|
|
12940
13332
|
if (occurrences.length === 0) return reject(["NO_RELATIVE_DATE"]);
|
|
12941
|
-
const spine =
|
|
13333
|
+
const spine = collectServerFunctionLeavesOnAndSpine(stmt.where, resolveField2);
|
|
12942
13334
|
if (!spine.ok) return reject(spine.reasonCodes);
|
|
12943
13335
|
if (!sameRelativeMultiset(occurrences, spine.leaves)) {
|
|
12944
13336
|
return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
@@ -12961,7 +13353,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
12961
13353
|
const containsFunctions = testSeam.containsFunctions ?? serializationContainsFunctions;
|
|
12962
13354
|
const confirmedLeaves = [];
|
|
12963
13355
|
for (const leaf of spine.leaves) {
|
|
12964
|
-
const name =
|
|
13356
|
+
const name = serverFunctionNameOf(leaf);
|
|
12965
13357
|
if (name === null) return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
12966
13358
|
let query;
|
|
12967
13359
|
try {
|
|
@@ -12990,12 +13382,12 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
12990
13382
|
} catch {
|
|
12991
13383
|
return reject(["PREFILTER_SERIALIZATION_FAILED"]);
|
|
12992
13384
|
}
|
|
12993
|
-
const expectedNames = confirmedLeaves.map((leaf) =>
|
|
12994
|
-
if (!containsFunctions(prefilterQuery, [...new Set(expectedNames)]) || !
|
|
13385
|
+
const expectedNames = confirmedLeaves.map((leaf) => serverFunctionNameOf(leaf));
|
|
13386
|
+
if (!containsFunctions(prefilterQuery, [...new Set(expectedNames)]) || !serializedMultisetContains2(prefilterQuery, expectedNames)) {
|
|
12995
13387
|
return reject(["PREFILTER_FUNCTION_MISSING"]);
|
|
12996
13388
|
}
|
|
12997
13389
|
const residualWhere = testSeam.rewriteResidual ? testSeam.rewriteResidual(stmt.where, adoptedLeaves) : replaceAdoptedLeaves(stmt.where, adoptedLeaves);
|
|
12998
|
-
if (residualWhere !== null &&
|
|
13390
|
+
if (residualWhere !== null && collectServerFunctionOccurrences(residualWhere).length > 0) {
|
|
12999
13391
|
return reject(["RESIDUAL_RELATIVE_DATE_REMAINED"]);
|
|
13000
13392
|
}
|
|
13001
13393
|
if (residualWhere === null) {
|
|
@@ -13016,7 +13408,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13016
13408
|
}
|
|
13017
13409
|
};
|
|
13018
13410
|
}
|
|
13019
|
-
function
|
|
13411
|
+
function collectServerFunctionLeavesOnAndSpine(where, resolveField2) {
|
|
13020
13412
|
const leaves = [];
|
|
13021
13413
|
let failure = null;
|
|
13022
13414
|
const visit = (node) => {
|
|
@@ -13031,24 +13423,19 @@ function collectRelativeLeavesOnAndSpine(where, resolveField2) {
|
|
|
13031
13423
|
visit(node.right);
|
|
13032
13424
|
return;
|
|
13033
13425
|
}
|
|
13034
|
-
if (
|
|
13426
|
+
if (collectServerFunctionOccurrences(node).length > 0) {
|
|
13035
13427
|
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
13036
13428
|
}
|
|
13037
13429
|
return;
|
|
13038
13430
|
case "NOT":
|
|
13039
|
-
if (
|
|
13431
|
+
if (collectServerFunctionOccurrences(node).length > 0) {
|
|
13040
13432
|
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
13041
13433
|
}
|
|
13042
13434
|
return;
|
|
13043
13435
|
case "BINARY": {
|
|
13044
|
-
const name =
|
|
13436
|
+
const name = serverFunctionNameOf(node);
|
|
13045
13437
|
if (name === null) return;
|
|
13046
|
-
const result =
|
|
13047
|
-
node.op,
|
|
13048
|
-
node.left,
|
|
13049
|
-
node.right,
|
|
13050
|
-
resolveField2
|
|
13051
|
-
);
|
|
13438
|
+
const result = classifyWhereCapability(node, resolveField2);
|
|
13052
13439
|
if (result.capability !== "EXACT_PUSHDOWN") {
|
|
13053
13440
|
failure = "RELATIVE_DATE_LEAF_NOT_EXACT";
|
|
13054
13441
|
return;
|
|
@@ -13057,7 +13444,7 @@ function collectRelativeLeavesOnAndSpine(where, resolveField2) {
|
|
|
13057
13444
|
return;
|
|
13058
13445
|
}
|
|
13059
13446
|
case "EXISTS":
|
|
13060
|
-
if (
|
|
13447
|
+
if (collectServerFunctionOccurrences(node).length > 0) {
|
|
13061
13448
|
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
13062
13449
|
}
|
|
13063
13450
|
return;
|
|
@@ -13069,13 +13456,19 @@ function collectRelativeLeavesOnAndSpine(where, resolveField2) {
|
|
|
13069
13456
|
visit(where);
|
|
13070
13457
|
return failure === null ? { ok: true, leaves } : { ok: false, reasonCodes: [failure] };
|
|
13071
13458
|
}
|
|
13072
|
-
function
|
|
13073
|
-
|
|
13459
|
+
function serverFunctionNameOf(leaf) {
|
|
13460
|
+
if (leaf.right.type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(leaf.right.name)) {
|
|
13461
|
+
return leaf.right.name;
|
|
13462
|
+
}
|
|
13463
|
+
if (leaf.right.type === "IN_LIST" && leaf.right.values.length === 1 && leaf.right.values[0].type === "KINTONE_FUNC" && isServerOnlyWhereFunctionName(leaf.right.values[0].name)) {
|
|
13464
|
+
return leaf.right.values[0].name;
|
|
13465
|
+
}
|
|
13466
|
+
return null;
|
|
13074
13467
|
}
|
|
13075
|
-
function
|
|
13468
|
+
function collectServerFunctionOccurrences(where) {
|
|
13076
13469
|
const found = [];
|
|
13077
13470
|
const visitWhere = (node) => {
|
|
13078
|
-
if (node.type === "BINARY" &&
|
|
13471
|
+
if (node.type === "BINARY" && serverFunctionNameOf(node) !== null) {
|
|
13079
13472
|
found.push(node);
|
|
13080
13473
|
return;
|
|
13081
13474
|
}
|
|
@@ -13213,10 +13606,10 @@ function collectFieldMetadata(where, resolveField2) {
|
|
|
13213
13606
|
}
|
|
13214
13607
|
return { fieldTypes, fieldOptions };
|
|
13215
13608
|
}
|
|
13216
|
-
function
|
|
13609
|
+
function serializedMultisetContains2(query, expectedNames) {
|
|
13217
13610
|
const expected = /* @__PURE__ */ new Map();
|
|
13218
13611
|
for (const name of expectedNames) {
|
|
13219
|
-
if (!
|
|
13612
|
+
if (!SERVER_ONLY_WHERE_FUNCTION_NAMES.has(name)) return false;
|
|
13220
13613
|
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
13221
13614
|
}
|
|
13222
13615
|
for (const [name, count] of expected) {
|
|
@@ -15370,7 +15763,7 @@ async function resolveSelectWhereCapability(stmt, client, cacheContext, material
|
|
|
15370
15763
|
}
|
|
15371
15764
|
function formatWhereCapabilityFailure(result) {
|
|
15372
15765
|
const reason = result.reasons.find(
|
|
15373
|
-
(candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED"
|
|
15766
|
+
(candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED" || candidate.code === "WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE"
|
|
15374
15767
|
) ?? result.reasons[0];
|
|
15375
15768
|
const details = [
|
|
15376
15769
|
reason?.field ? `field=${reason.field}` : null,
|
|
@@ -15436,7 +15829,9 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15436
15829
|
if (whereCapability.capability === "UNSUPPORTED") {
|
|
15437
15830
|
throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
|
|
15438
15831
|
}
|
|
15832
|
+
const staticMode = resolveSelectMode(stmt);
|
|
15439
15833
|
let prefilterPlan;
|
|
15834
|
+
let fullScanExactPlan;
|
|
15440
15835
|
if (whereCapability.capability === "SUPERSET_PREFILTER") {
|
|
15441
15836
|
const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, cteCache);
|
|
15442
15837
|
const decomposition = decomposeRelativeDatePrefilter(stmt, resolver);
|
|
@@ -15444,7 +15839,23 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15444
15839
|
prefilterPlan = decomposition.plan;
|
|
15445
15840
|
}
|
|
15446
15841
|
}
|
|
15447
|
-
|
|
15842
|
+
if (prefilterPlan === void 0 && whereCapability.capability === "EXACT_PUSHDOWN" && stmt.where !== null) {
|
|
15843
|
+
let serializedWholeWhere = null;
|
|
15844
|
+
try {
|
|
15845
|
+
serializedWholeWhere = whereToKintone(stmt.where);
|
|
15846
|
+
} catch {
|
|
15847
|
+
serializedWholeWhere = null;
|
|
15848
|
+
}
|
|
15849
|
+
fullScanExactPlan = buildRelativeDateFullScanExactPlan({
|
|
15850
|
+
select: stmt,
|
|
15851
|
+
selectMode: staticMode,
|
|
15852
|
+
capability: whereCapability,
|
|
15853
|
+
context: { allowFullScanExact: true },
|
|
15854
|
+
serializedWholeWhere,
|
|
15855
|
+
relativeFunctionNames: serverOnlyFunctionOccurrencesInWhere(stmt.where)
|
|
15856
|
+
}) ?? void 0;
|
|
15857
|
+
if (fullScanExactPlan) prefilterPlan = fullScanExactPlan.prefilterPlan;
|
|
15858
|
+
}
|
|
15448
15859
|
const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
|
|
15449
15860
|
const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
|
|
15450
15861
|
const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
|
|
@@ -15462,18 +15873,31 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15462
15873
|
client,
|
|
15463
15874
|
cacheContext
|
|
15464
15875
|
);
|
|
15465
|
-
const completePolicy = buildCompleteInputPolicy(
|
|
15876
|
+
const completePolicy = buildCompleteInputPolicy(
|
|
15877
|
+
stmt,
|
|
15878
|
+
options,
|
|
15879
|
+
orderPlan
|
|
15880
|
+
);
|
|
15881
|
+
const failClosedForB72Local = fullScanExactPlan !== void 0 && (staticMode === "FULL_SCAN" || orderPlan?.kind === "CANONICAL_LOCAL");
|
|
15882
|
+
const executionClient = failClosedForB72Local ? wrapClientWithSearchAbort(client, { aborted: false }, true) : client;
|
|
15466
15883
|
try {
|
|
15467
15884
|
if (mode === "SIMPLE") {
|
|
15468
|
-
result = await executeSimpleSelect(
|
|
15885
|
+
result = await executeSimpleSelect(
|
|
15886
|
+
stmt,
|
|
15887
|
+
executionClient,
|
|
15888
|
+
completePolicy.effectiveOptions,
|
|
15889
|
+
cacheContext,
|
|
15890
|
+
orderPlan,
|
|
15891
|
+
orderMeta
|
|
15892
|
+
);
|
|
15469
15893
|
} else {
|
|
15470
15894
|
result = await executeFullScanSelect(
|
|
15471
15895
|
stmt,
|
|
15472
|
-
|
|
15896
|
+
executionClient,
|
|
15473
15897
|
completePolicy.effectiveOptions,
|
|
15474
15898
|
cacheContext,
|
|
15475
15899
|
cteCache,
|
|
15476
|
-
whereCapability.capability === "EXACT_PUSHDOWN",
|
|
15900
|
+
whereCapability.capability === "EXACT_PUSHDOWN" && prefilterPlan === void 0,
|
|
15477
15901
|
orderMeta,
|
|
15478
15902
|
prefilterPlan,
|
|
15479
15903
|
plainGroupByPlan
|
|
@@ -20510,7 +20934,7 @@ function renderResidualValue(node) {
|
|
|
20510
20934
|
case "CONCAT_OP":
|
|
20511
20935
|
return `(${renderResidualValue(value["left"])} ${typeof value["op"] === "string" ? value["op"] : value["type"] === "CONCAT_OP" ? "||" : "<op>"} ${renderResidualValue(value["right"])})`;
|
|
20512
20936
|
case "KINTONE_FUNC":
|
|
20513
|
-
return typeof value["name"] === "string" ? `${value["name"]}(...)` : "<expr>";
|
|
20937
|
+
return typeof value["name"] === "string" ? value["name"] === "LOGINUSER" ? "LOGINUSER()" : `${value["name"]}(...)` : "<expr>";
|
|
20514
20938
|
case "IN_LIST":
|
|
20515
20939
|
return Array.isArray(value["values"]) ? `(${value["values"].map(renderResidualValue).join(", ")})` : "<expr>";
|
|
20516
20940
|
case "ARRAY":
|
|
@@ -20546,10 +20970,11 @@ function renderRelativeDateResidualWhere(where) {
|
|
|
20546
20970
|
}
|
|
20547
20971
|
}
|
|
20548
20972
|
function relativeDateExplainLines(plan) {
|
|
20549
|
-
if (!plan.
|
|
20973
|
+
if (!plan.hasServerOnlyWhereFunction) return [];
|
|
20550
20974
|
if (!plan.allowed && plan.rejection) {
|
|
20975
|
+
const label = isRelativeDateFunctionName(plan.rejection.functionName) ? "relative date function" : "kintone function";
|
|
20551
20976
|
return [
|
|
20552
|
-
`
|
|
20977
|
+
` ${label}: ${plan.rejection.functionName}`,
|
|
20553
20978
|
" plan status: rejected",
|
|
20554
20979
|
` reason: ${plan.rejection.reasonCodes.join(", ")}`,
|
|
20555
20980
|
" client evaluation: forbidden",
|
|
@@ -20558,18 +20983,46 @@ function relativeDateExplainLines(plan) {
|
|
|
20558
20983
|
}
|
|
20559
20984
|
const lines = [];
|
|
20560
20985
|
for (const node of plan.nodes) {
|
|
20986
|
+
const fullScanExactPlan = node.fullScanExactPlan;
|
|
20987
|
+
if (node.allowed && node.allowForm === "FULL_SCAN_EXACT" && fullScanExactPlan) {
|
|
20988
|
+
for (const leaf of fullScanExactPlan.prefilterPlan.exactRelativeLeaves) {
|
|
20989
|
+
const functionName = serverFunctionNameOfExplainLeaf(leaf);
|
|
20990
|
+
const field = leaf.left.type === "FIELD" ? leaf.left.field : void 0;
|
|
20991
|
+
const operator = relativeReasonOperator(leaf.op);
|
|
20992
|
+
const detail = node.capability?.reasons.find(
|
|
20993
|
+
(reason) => reason.functionName === functionName && (field === void 0 || reason.field === field) && reason.operator === operator
|
|
20994
|
+
);
|
|
20995
|
+
lines.push(
|
|
20996
|
+
` ${serverFunctionLabel(functionName)}: ${functionName}`,
|
|
20997
|
+
` ${serverFunctionEvaluationLabel(functionName)}: kintone server whole-WHERE exact`,
|
|
20998
|
+
` field: ${detail?.field ?? field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
20999
|
+
` operator: ${detail?.operator ?? operator}`
|
|
21000
|
+
);
|
|
21001
|
+
}
|
|
21002
|
+
const wholeWhereQuery = fullScanExactPlan.serializedWholeWhere;
|
|
21003
|
+
lines.push(
|
|
21004
|
+
" where capability: EXACT_PUSHDOWN",
|
|
21005
|
+
` server predicate: ${wholeWhereQuery}`,
|
|
21006
|
+
" client residual: (none)",
|
|
21007
|
+
` ${serverFunctionClientEvaluationLabel(
|
|
21008
|
+
fullScanExactPlan.prefilterPlan.exactRelativeLeaves
|
|
21009
|
+
)}: 0`,
|
|
21010
|
+
` kintone query: ${wholeWhereQuery}`
|
|
21011
|
+
);
|
|
21012
|
+
continue;
|
|
21013
|
+
}
|
|
20561
21014
|
const prefilterPlan = node.prefilterPlan;
|
|
20562
21015
|
if (node.allowed && prefilterPlan?.prefilterWhere && prefilterPlan.residualWhere) {
|
|
20563
21016
|
for (const leaf of prefilterPlan.exactRelativeLeaves) {
|
|
20564
|
-
const functionName = leaf
|
|
21017
|
+
const functionName = serverFunctionNameOfExplainLeaf(leaf);
|
|
20565
21018
|
const field = leaf.left.type === "FIELD" ? leaf.left.field : void 0;
|
|
20566
21019
|
const operator = relativeReasonOperator(leaf.op);
|
|
20567
21020
|
const detail = node.capability?.reasons.find(
|
|
20568
21021
|
(reason) => reason.functionName === functionName && (field === void 0 || reason.field === field) && reason.operator === operator
|
|
20569
21022
|
);
|
|
20570
21023
|
lines.push(
|
|
20571
|
-
`
|
|
20572
|
-
|
|
21024
|
+
` ${serverFunctionLabel(functionName)}: ${functionName}`,
|
|
21025
|
+
` ${serverFunctionEvaluationLabel(functionName)}: kintone server exact prefilter`,
|
|
20573
21026
|
` field: ${detail?.field ?? field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
20574
21027
|
` operator: ${detail?.operator ?? operator}`
|
|
20575
21028
|
);
|
|
@@ -20579,7 +21032,7 @@ function relativeDateExplainLines(plan) {
|
|
|
20579
21032
|
" where capability: SUPERSET_PREFILTER",
|
|
20580
21033
|
` server prefilter: ${serverPrefilter}`,
|
|
20581
21034
|
` client residual: ${renderRelativeDateResidualWhere(prefilterPlan.residualWhere)}`,
|
|
20582
|
-
|
|
21035
|
+
` ${serverFunctionClientEvaluationLabel(prefilterPlan.exactRelativeLeaves)}: 0`,
|
|
20583
21036
|
` kintone query: ${serverPrefilter}`
|
|
20584
21037
|
);
|
|
20585
21038
|
continue;
|
|
@@ -20588,6 +21041,19 @@ function relativeDateExplainLines(plan) {
|
|
|
20588
21041
|
const detail = node.capability?.reasons.find(
|
|
20589
21042
|
(reason) => reason.functionName === functionName
|
|
20590
21043
|
);
|
|
21044
|
+
if (!isRelativeDateFunctionName(functionName)) {
|
|
21045
|
+
lines.push(
|
|
21046
|
+
` kintone function: ${functionName}`,
|
|
21047
|
+
" kintone function evaluation: kintone server",
|
|
21048
|
+
` field: ${detail?.field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
21049
|
+
` operator: ${detail?.operator ?? "(unknown)"}`,
|
|
21050
|
+
` where capability: ${node.capability?.capability ?? "(unknown)"}`,
|
|
21051
|
+
" client residual: (none)",
|
|
21052
|
+
" kintone function client evaluations: 0",
|
|
21053
|
+
` kintone query: ${node.restQuery || "(\u306A\u3057)"}`
|
|
21054
|
+
);
|
|
21055
|
+
continue;
|
|
21056
|
+
}
|
|
20591
21057
|
lines.push(
|
|
20592
21058
|
` relative date function: ${functionName}`,
|
|
20593
21059
|
" evaluation: kintone server",
|
|
@@ -20601,6 +21067,24 @@ function relativeDateExplainLines(plan) {
|
|
|
20601
21067
|
}
|
|
20602
21068
|
return lines;
|
|
20603
21069
|
}
|
|
21070
|
+
function serverFunctionLabel(functionName) {
|
|
21071
|
+
return isRelativeDateFunctionName(functionName) ? "relative date function" : "kintone function";
|
|
21072
|
+
}
|
|
21073
|
+
function serverFunctionNameOfExplainLeaf(leaf) {
|
|
21074
|
+
if (leaf.right.type === "KINTONE_FUNC") return leaf.right.name;
|
|
21075
|
+
if (leaf.right.type === "IN_LIST" && leaf.right.values.length === 1 && leaf.right.values[0].type === "KINTONE_FUNC") {
|
|
21076
|
+
return leaf.right.values[0].name;
|
|
21077
|
+
}
|
|
21078
|
+
return "(unknown)";
|
|
21079
|
+
}
|
|
21080
|
+
function serverFunctionEvaluationLabel(functionName) {
|
|
21081
|
+
return isRelativeDateFunctionName(functionName) ? "relative date evaluation" : "kintone function evaluation";
|
|
21082
|
+
}
|
|
21083
|
+
function serverFunctionClientEvaluationLabel(leaves) {
|
|
21084
|
+
return leaves.every(
|
|
21085
|
+
(leaf) => leaf.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(leaf.right.name)
|
|
21086
|
+
) ? "relative date client evaluations" : "kintone function client evaluations";
|
|
21087
|
+
}
|
|
20604
21088
|
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
|
|
20605
21089
|
const statements = parseSqlBatch(sql, enableImport);
|
|
20606
21090
|
const analysis = analyzeBatch(statements);
|
|
@@ -20619,7 +21103,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
20619
21103
|
maxRecords,
|
|
20620
21104
|
relativeDatePlan
|
|
20621
21105
|
);
|
|
20622
|
-
const statementPlan = relativeDatePlan.
|
|
21106
|
+
const statementPlan = relativeDatePlan.hasServerOnlyWhereFunction && !relativeDatePlan.allowed ? relativeDateExplainLines(relativeDatePlan) : [
|
|
20623
21107
|
...relativeDateExplainLines(relativeDatePlan),
|
|
20624
21108
|
...addCursorConcurrency(buildBatchStatementPlan(
|
|
20625
21109
|
planStmt,
|
|
@@ -20748,7 +21232,7 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
|
|
|
20748
21232
|
sharedPlan
|
|
20749
21233
|
);
|
|
20750
21234
|
const relativeLines = relativeDateExplainLines(sharedPlan);
|
|
20751
|
-
const lines = sharedPlan.
|
|
21235
|
+
const lines = sharedPlan.hasServerOnlyWhereFunction && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
|
|
20752
21236
|
...explainMetadataLines(analysis),
|
|
20753
21237
|
...relativeLines,
|
|
20754
21238
|
...addCursorConcurrency(
|