@rex0220/kintone-sql-tools 3.24.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 +360 -90
- 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 +7 -7
- package/dist-engine/meta/cjs.json +27 -22
- package/dist-engine/meta/esm.json +27 -22
- package/dist-engine/meta/umd.json +27 -22
- package/dist-mcp/ksql-mcp.js +363 -93
- 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,34 @@ 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
|
+
}
|
|
12606
12785
|
|
|
12607
12786
|
// src/core/optimization/relativeDateFullScanExactPlan.ts
|
|
12608
12787
|
function buildRelativeDateFullScanExactPlan(input) {
|
|
@@ -12623,7 +12802,7 @@ function buildRelativeDateFullScanExactPlan(input) {
|
|
|
12623
12802
|
const hasCanonicalOrder2 = select.orderMode === "CANONICAL" && select.orderBy.length > 0;
|
|
12624
12803
|
if (selectMode !== "FULL_SCAN" && !hasCanonicalOrder2) return null;
|
|
12625
12804
|
if (capability.capability !== "EXACT_PUSHDOWN") return null;
|
|
12626
|
-
const occurrences =
|
|
12805
|
+
const occurrences = serverOnlyFunctionOccurrencesInWhere(select.where);
|
|
12627
12806
|
if (occurrences.length === 0) return null;
|
|
12628
12807
|
if (!sameOccurrenceList(occurrences, relativeFunctionNames)) return null;
|
|
12629
12808
|
if (serializedWholeWhere === null || !serializedMultisetContains(serializedWholeWhere, occurrences)) {
|
|
@@ -12632,7 +12811,7 @@ function buildRelativeDateFullScanExactPlan(input) {
|
|
|
12632
12811
|
const prefilterPlan = {
|
|
12633
12812
|
prefilterWhere: select.where,
|
|
12634
12813
|
residualWhere: null,
|
|
12635
|
-
exactRelativeLeaves:
|
|
12814
|
+
exactRelativeLeaves: collectExactServerFunctionLeaves(select.where),
|
|
12636
12815
|
relativeFunctionNames: new Set(occurrences),
|
|
12637
12816
|
appliedKlikes: /* @__PURE__ */ new Set(),
|
|
12638
12817
|
capability: capability.capability,
|
|
@@ -12670,7 +12849,7 @@ function assertRelativeDateFullScanExactPlan(plan, input, occurrences) {
|
|
|
12670
12849
|
throw new Error("FULL_SCAN_EXACT invariant: relative occurrence serialization");
|
|
12671
12850
|
}
|
|
12672
12851
|
}
|
|
12673
|
-
function
|
|
12852
|
+
function serverOnlyFunctionOccurrencesInWhere(where) {
|
|
12674
12853
|
const names = [];
|
|
12675
12854
|
const visit = (node) => {
|
|
12676
12855
|
if (Array.isArray(node)) {
|
|
@@ -12680,7 +12859,7 @@ function relativeDateFunctionOccurrencesInWhere(where) {
|
|
|
12680
12859
|
if (node === null || typeof node !== "object") return;
|
|
12681
12860
|
const value = node;
|
|
12682
12861
|
if (value["type"] === "SELECT") return;
|
|
12683
|
-
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" &&
|
|
12862
|
+
if (value["type"] === "KINTONE_FUNC" && typeof value["name"] === "string" && isServerOnlyWhereFunctionName(value["name"])) {
|
|
12684
12863
|
names.push(value["name"]);
|
|
12685
12864
|
return;
|
|
12686
12865
|
}
|
|
@@ -12689,12 +12868,12 @@ function relativeDateFunctionOccurrencesInWhere(where) {
|
|
|
12689
12868
|
visit(where);
|
|
12690
12869
|
return names;
|
|
12691
12870
|
}
|
|
12692
|
-
function
|
|
12871
|
+
function collectExactServerFunctionLeaves(where) {
|
|
12693
12872
|
const leaves = [];
|
|
12694
12873
|
const visit = (node) => {
|
|
12695
12874
|
switch (node.type) {
|
|
12696
12875
|
case "BINARY":
|
|
12697
|
-
if (node.right.type === "KINTONE_FUNC" &&
|
|
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)) {
|
|
12698
12877
|
leaves.push(node);
|
|
12699
12878
|
}
|
|
12700
12879
|
return;
|
|
@@ -12721,7 +12900,7 @@ function sameOccurrenceList(actual, expected) {
|
|
|
12721
12900
|
function serializedMultisetContains(query, expectedNames) {
|
|
12722
12901
|
const expected = /* @__PURE__ */ new Map();
|
|
12723
12902
|
for (const name of expectedNames) {
|
|
12724
|
-
if (!
|
|
12903
|
+
if (!isServerOnlyWhereFunctionName(name)) return false;
|
|
12725
12904
|
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
12726
12905
|
}
|
|
12727
12906
|
for (const [name, count] of expected) {
|
|
@@ -12742,7 +12921,7 @@ function relativeDateFunctionNamesInNode(node, stopAtNestedSelect) {
|
|
|
12742
12921
|
}
|
|
12743
12922
|
if (valueNode === null || typeof valueNode !== "object") return;
|
|
12744
12923
|
const value = valueNode;
|
|
12745
|
-
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"])) {
|
|
12746
12925
|
seen.add(value["name"]);
|
|
12747
12926
|
names.push(value["name"]);
|
|
12748
12927
|
return;
|
|
@@ -12776,7 +12955,7 @@ function nestedSelects(node, root) {
|
|
|
12776
12955
|
visit(node);
|
|
12777
12956
|
return found;
|
|
12778
12957
|
}
|
|
12779
|
-
function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = true, allowFullScanExact = true) {
|
|
12958
|
+
function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = true, allowFullScanExact = true, forceNestedForbidden = forceForbidden) {
|
|
12780
12959
|
const functionNames = relativeDateFunctionNamesInWhere(select.where);
|
|
12781
12960
|
if (functionNames.length > 0) {
|
|
12782
12961
|
candidates.push({
|
|
@@ -12787,7 +12966,7 @@ function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = t
|
|
|
12787
12966
|
path,
|
|
12788
12967
|
allowPhase2Prefilter: allowPhase2,
|
|
12789
12968
|
allowFullScanExact,
|
|
12790
|
-
relativeFunctionOccurrences:
|
|
12969
|
+
relativeFunctionOccurrences: serverOnlyFunctionOccurrencesInWhere(select.where)
|
|
12791
12970
|
});
|
|
12792
12971
|
}
|
|
12793
12972
|
nestedSelects(select, select).forEach(
|
|
@@ -12795,9 +12974,10 @@ function collectSelect(select, path, candidates, forceForbidden, allowPhase2 = t
|
|
|
12795
12974
|
nested,
|
|
12796
12975
|
`${path}.select-source[${index}]`,
|
|
12797
12976
|
candidates,
|
|
12798
|
-
|
|
12799
|
-
allowPhase2,
|
|
12800
|
-
allowFullScanExact
|
|
12977
|
+
forceNestedForbidden,
|
|
12978
|
+
forceNestedForbidden ? false : allowPhase2,
|
|
12979
|
+
forceNestedForbidden ? false : allowFullScanExact,
|
|
12980
|
+
forceNestedForbidden
|
|
12801
12981
|
)
|
|
12802
12982
|
);
|
|
12803
12983
|
}
|
|
@@ -12811,18 +12991,34 @@ function collectUnion(union, path, candidates, forceForbidden, allowFullScanExac
|
|
|
12811
12991
|
}
|
|
12812
12992
|
function collectWith(statement, path, candidates, inheritedForbidden) {
|
|
12813
12993
|
if (!inheritedForbidden && canInlineSingleCte(statement)) {
|
|
12814
|
-
collectSelect(buildInlinedQuery(statement), `${path}.inlined`, candidates, false, true,
|
|
12994
|
+
collectSelect(buildInlinedQuery(statement), `${path}.inlined`, candidates, false, true, true);
|
|
12815
12995
|
return;
|
|
12816
12996
|
}
|
|
12817
12997
|
statement.ctes.forEach((cte, index) => {
|
|
12818
12998
|
if (cte.query.type === "SELECT") {
|
|
12819
|
-
collectSelect(
|
|
12999
|
+
collectSelect(
|
|
13000
|
+
cte.query,
|
|
13001
|
+
`${path}.cte[${index}]`,
|
|
13002
|
+
candidates,
|
|
13003
|
+
inheritedForbidden,
|
|
13004
|
+
false,
|
|
13005
|
+
!inheritedForbidden,
|
|
13006
|
+
true
|
|
13007
|
+
);
|
|
12820
13008
|
} else if (cte.query.type === "UNION") {
|
|
12821
13009
|
collectUnion(cte.query, `${path}.cte[${index}]`, candidates, true, false);
|
|
12822
13010
|
}
|
|
12823
13011
|
});
|
|
12824
13012
|
if (statement.query.type === "SELECT") {
|
|
12825
|
-
collectSelect(
|
|
13013
|
+
collectSelect(
|
|
13014
|
+
statement.query,
|
|
13015
|
+
`${path}.main`,
|
|
13016
|
+
candidates,
|
|
13017
|
+
inheritedForbidden,
|
|
13018
|
+
false,
|
|
13019
|
+
!inheritedForbidden,
|
|
13020
|
+
true
|
|
13021
|
+
);
|
|
12826
13022
|
} else {
|
|
12827
13023
|
collectUnion(statement.query, `${path}.main`, candidates, true, false);
|
|
12828
13024
|
}
|
|
@@ -12839,9 +13035,9 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12839
13035
|
collectWith(statement, path, candidates, forceForbidden);
|
|
12840
13036
|
return;
|
|
12841
13037
|
case "CREATE_TEMP_TABLE":
|
|
12842
|
-
if (statement.query.type === "WITH") collectWith(statement.query, `${path}.query`, candidates,
|
|
13038
|
+
if (statement.query.type === "WITH") collectWith(statement.query, `${path}.query`, candidates, false);
|
|
12843
13039
|
else if (statement.query.type === "UNION") collectUnion(statement.query, `${path}.query`, candidates, true, false);
|
|
12844
|
-
else collectSelect(statement.query, `${path}.query`, candidates,
|
|
13040
|
+
else collectSelect(statement.query, `${path}.query`, candidates, false, false, true, true);
|
|
12845
13041
|
return;
|
|
12846
13042
|
case "EXPLAIN":
|
|
12847
13043
|
collectStatement(statement.query, `${path}.query`, candidates, forceForbidden);
|
|
@@ -12857,7 +13053,7 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12857
13053
|
functionNames,
|
|
12858
13054
|
path,
|
|
12859
13055
|
allowFullScanExact: false,
|
|
12860
|
-
relativeFunctionOccurrences:
|
|
13056
|
+
relativeFunctionOccurrences: serverOnlyFunctionOccurrencesInWhere(statement.where)
|
|
12861
13057
|
});
|
|
12862
13058
|
}
|
|
12863
13059
|
nestedSelects(statement, statement).forEach(
|
|
@@ -12877,7 +13073,7 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12877
13073
|
functionNames,
|
|
12878
13074
|
path,
|
|
12879
13075
|
allowFullScanExact: false,
|
|
12880
|
-
relativeFunctionOccurrences:
|
|
13076
|
+
relativeFunctionOccurrences: serverOnlyFunctionOccurrencesInWhere(statement.where)
|
|
12881
13077
|
});
|
|
12882
13078
|
}
|
|
12883
13079
|
if (statement.type === "UPDATE" && statement.applyBlocks?.length) {
|
|
@@ -12920,7 +13116,16 @@ function collectStatement(statement, path, candidates, forceForbidden = false) {
|
|
|
12920
13116
|
}
|
|
12921
13117
|
}
|
|
12922
13118
|
function serializationContainsFunctions(query, names) {
|
|
12923
|
-
|
|
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;
|
|
12924
13129
|
}
|
|
12925
13130
|
function allowRelativeDatePrefilterPlan(select, decomposition) {
|
|
12926
13131
|
return decomposition.eligible === true && resolveSelectMode(select) === "FULL_SCAN" && select.orderMode !== "KINTONE_NATIVE" && select.from.cteName === null && !select.from.subtableCode && select.joins.length === 0;
|
|
@@ -12935,23 +13140,36 @@ function rejectedNode(candidate) {
|
|
|
12935
13140
|
allowed: false
|
|
12936
13141
|
};
|
|
12937
13142
|
}
|
|
12938
|
-
function
|
|
13143
|
+
function serverFunctionReasonCodes(functionName, capability) {
|
|
12939
13144
|
const codes = (capability?.reasons ?? []).filter(
|
|
12940
|
-
(reason) => reason.functionName
|
|
12941
|
-
).map((reason) => reason.code).filter(
|
|
12942
|
-
|
|
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)) {
|
|
12943
13159
|
codes.push(WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN);
|
|
12944
13160
|
}
|
|
12945
13161
|
return [...new Set(codes)];
|
|
12946
13162
|
}
|
|
12947
13163
|
function rejectionFor(candidate, capability) {
|
|
12948
|
-
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);
|
|
12949
13167
|
return {
|
|
12950
|
-
functionName
|
|
13168
|
+
functionName,
|
|
12951
13169
|
path: candidate.path,
|
|
12952
13170
|
code: reasonCodes.find(
|
|
12953
|
-
(code) => code !==
|
|
12954
|
-
) ??
|
|
13171
|
+
(code) => code !== requiresExact
|
|
13172
|
+
) ?? requiresExact,
|
|
12955
13173
|
reasonCodes
|
|
12956
13174
|
};
|
|
12957
13175
|
}
|
|
@@ -12964,7 +13182,8 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12964
13182
|
const node2 = rejectedNode(candidate);
|
|
12965
13183
|
nodes.push(node2);
|
|
12966
13184
|
return {
|
|
12967
|
-
hasRelativeDate:
|
|
13185
|
+
hasRelativeDate: candidate.functionNames.some(isRelativeDateFunctionName),
|
|
13186
|
+
hasServerOnlyWhereFunction: true,
|
|
12968
13187
|
nodes,
|
|
12969
13188
|
allowed: false,
|
|
12970
13189
|
rejection: rejectionFor(candidate)
|
|
@@ -12981,7 +13200,10 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
12981
13200
|
} catch {
|
|
12982
13201
|
restQuery2 = "";
|
|
12983
13202
|
}
|
|
12984
|
-
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
|
+
);
|
|
12985
13207
|
let prefilterPlan;
|
|
12986
13208
|
let phase2PrefilterEligible;
|
|
12987
13209
|
if (!allowed2 && candidate.allowPhase2Prefilter !== false && capability2.capability === "SUPERSET_PREFILTER" && resolver.prefilterDecomposition) {
|
|
@@ -13023,7 +13245,10 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13023
13245
|
nodes.push(node2);
|
|
13024
13246
|
if (!allowed2) {
|
|
13025
13247
|
return {
|
|
13026
|
-
hasRelativeDate:
|
|
13248
|
+
hasRelativeDate: candidates.some(
|
|
13249
|
+
(entry) => entry.functionNames.some(isRelativeDateFunctionName)
|
|
13250
|
+
),
|
|
13251
|
+
hasServerOnlyWhereFunction: true,
|
|
13027
13252
|
nodes,
|
|
13028
13253
|
allowed: false,
|
|
13029
13254
|
rejection: rejectionFor(candidate, capability2)
|
|
@@ -13039,7 +13264,10 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13039
13264
|
} catch {
|
|
13040
13265
|
restQuery = "";
|
|
13041
13266
|
}
|
|
13042
|
-
const allowed = capability.capability === "EXACT_PUSHDOWN" && serializationContainsFunctions(
|
|
13267
|
+
const allowed = capability.capability === "EXACT_PUSHDOWN" && serializationContainsFunctions(
|
|
13268
|
+
restQuery,
|
|
13269
|
+
candidate.relativeFunctionOccurrences
|
|
13270
|
+
);
|
|
13043
13271
|
const node = {
|
|
13044
13272
|
kind: candidate.kind,
|
|
13045
13273
|
source: candidate.source,
|
|
@@ -13053,7 +13281,10 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13053
13281
|
nodes.push(node);
|
|
13054
13282
|
if (!allowed) {
|
|
13055
13283
|
return {
|
|
13056
|
-
hasRelativeDate:
|
|
13284
|
+
hasRelativeDate: candidates.some(
|
|
13285
|
+
(entry) => entry.functionNames.some(isRelativeDateFunctionName)
|
|
13286
|
+
),
|
|
13287
|
+
hasServerOnlyWhereFunction: true,
|
|
13057
13288
|
nodes,
|
|
13058
13289
|
allowed: false,
|
|
13059
13290
|
rejection: rejectionFor(candidate, capability)
|
|
@@ -13061,18 +13292,22 @@ async function buildRelativeDatePushdownPlan(statement, resolver) {
|
|
|
13061
13292
|
}
|
|
13062
13293
|
}
|
|
13063
13294
|
return {
|
|
13064
|
-
hasRelativeDate: candidates.
|
|
13295
|
+
hasRelativeDate: candidates.some(
|
|
13296
|
+
(entry) => entry.functionNames.some(isRelativeDateFunctionName)
|
|
13297
|
+
),
|
|
13298
|
+
hasServerOnlyWhereFunction: candidates.length > 0,
|
|
13065
13299
|
nodes,
|
|
13066
13300
|
allowed: true
|
|
13067
13301
|
};
|
|
13068
13302
|
}
|
|
13069
13303
|
function assertRelativeDatePushdownPlan(plan) {
|
|
13070
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;
|
|
13071
13306
|
const details = plan.rejection.reasonCodes.filter(
|
|
13072
|
-
(code) => code !==
|
|
13307
|
+
(code) => code !== requiresExact
|
|
13073
13308
|
);
|
|
13074
13309
|
throw new Error(
|
|
13075
|
-
`${plan.rejection.functionName}: ${
|
|
13310
|
+
`${plan.rejection.functionName}: ${requiresExact}${details.length > 0 ? ` (reason=${details.join(", ")})` : ""} (path=${plan.rejection.path})`
|
|
13076
13311
|
);
|
|
13077
13312
|
}
|
|
13078
13313
|
}
|
|
@@ -13093,9 +13328,9 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13093
13328
|
if (stmt.from.cteName !== null || stmt.from.appId <= 0) {
|
|
13094
13329
|
return reject(["NOT_DIRECT_PHYSICAL_APP"]);
|
|
13095
13330
|
}
|
|
13096
|
-
const occurrences =
|
|
13331
|
+
const occurrences = collectServerFunctionOccurrences(stmt.where);
|
|
13097
13332
|
if (occurrences.length === 0) return reject(["NO_RELATIVE_DATE"]);
|
|
13098
|
-
const spine =
|
|
13333
|
+
const spine = collectServerFunctionLeavesOnAndSpine(stmt.where, resolveField2);
|
|
13099
13334
|
if (!spine.ok) return reject(spine.reasonCodes);
|
|
13100
13335
|
if (!sameRelativeMultiset(occurrences, spine.leaves)) {
|
|
13101
13336
|
return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
@@ -13118,7 +13353,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13118
13353
|
const containsFunctions = testSeam.containsFunctions ?? serializationContainsFunctions;
|
|
13119
13354
|
const confirmedLeaves = [];
|
|
13120
13355
|
for (const leaf of spine.leaves) {
|
|
13121
|
-
const name =
|
|
13356
|
+
const name = serverFunctionNameOf(leaf);
|
|
13122
13357
|
if (name === null) return reject(["RELATIVE_DATE_LEAF_COUNT_MISMATCH"]);
|
|
13123
13358
|
let query;
|
|
13124
13359
|
try {
|
|
@@ -13147,12 +13382,12 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13147
13382
|
} catch {
|
|
13148
13383
|
return reject(["PREFILTER_SERIALIZATION_FAILED"]);
|
|
13149
13384
|
}
|
|
13150
|
-
const expectedNames = confirmedLeaves.map((leaf) =>
|
|
13385
|
+
const expectedNames = confirmedLeaves.map((leaf) => serverFunctionNameOf(leaf));
|
|
13151
13386
|
if (!containsFunctions(prefilterQuery, [...new Set(expectedNames)]) || !serializedMultisetContains2(prefilterQuery, expectedNames)) {
|
|
13152
13387
|
return reject(["PREFILTER_FUNCTION_MISSING"]);
|
|
13153
13388
|
}
|
|
13154
13389
|
const residualWhere = testSeam.rewriteResidual ? testSeam.rewriteResidual(stmt.where, adoptedLeaves) : replaceAdoptedLeaves(stmt.where, adoptedLeaves);
|
|
13155
|
-
if (residualWhere !== null &&
|
|
13390
|
+
if (residualWhere !== null && collectServerFunctionOccurrences(residualWhere).length > 0) {
|
|
13156
13391
|
return reject(["RESIDUAL_RELATIVE_DATE_REMAINED"]);
|
|
13157
13392
|
}
|
|
13158
13393
|
if (residualWhere === null) {
|
|
@@ -13173,7 +13408,7 @@ function decomposeRelativeDatePrefilter(stmt, resolveField2, testSeam = {}) {
|
|
|
13173
13408
|
}
|
|
13174
13409
|
};
|
|
13175
13410
|
}
|
|
13176
|
-
function
|
|
13411
|
+
function collectServerFunctionLeavesOnAndSpine(where, resolveField2) {
|
|
13177
13412
|
const leaves = [];
|
|
13178
13413
|
let failure = null;
|
|
13179
13414
|
const visit = (node) => {
|
|
@@ -13188,24 +13423,19 @@ function collectRelativeLeavesOnAndSpine(where, resolveField2) {
|
|
|
13188
13423
|
visit(node.right);
|
|
13189
13424
|
return;
|
|
13190
13425
|
}
|
|
13191
|
-
if (
|
|
13426
|
+
if (collectServerFunctionOccurrences(node).length > 0) {
|
|
13192
13427
|
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
13193
13428
|
}
|
|
13194
13429
|
return;
|
|
13195
13430
|
case "NOT":
|
|
13196
|
-
if (
|
|
13431
|
+
if (collectServerFunctionOccurrences(node).length > 0) {
|
|
13197
13432
|
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
13198
13433
|
}
|
|
13199
13434
|
return;
|
|
13200
13435
|
case "BINARY": {
|
|
13201
|
-
const name =
|
|
13436
|
+
const name = serverFunctionNameOf(node);
|
|
13202
13437
|
if (name === null) return;
|
|
13203
|
-
const result =
|
|
13204
|
-
node.op,
|
|
13205
|
-
node.left,
|
|
13206
|
-
node.right,
|
|
13207
|
-
resolveField2
|
|
13208
|
-
);
|
|
13438
|
+
const result = classifyWhereCapability(node, resolveField2);
|
|
13209
13439
|
if (result.capability !== "EXACT_PUSHDOWN") {
|
|
13210
13440
|
failure = "RELATIVE_DATE_LEAF_NOT_EXACT";
|
|
13211
13441
|
return;
|
|
@@ -13214,7 +13444,7 @@ function collectRelativeLeavesOnAndSpine(where, resolveField2) {
|
|
|
13214
13444
|
return;
|
|
13215
13445
|
}
|
|
13216
13446
|
case "EXISTS":
|
|
13217
|
-
if (
|
|
13447
|
+
if (collectServerFunctionOccurrences(node).length > 0) {
|
|
13218
13448
|
failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
|
|
13219
13449
|
}
|
|
13220
13450
|
return;
|
|
@@ -13226,13 +13456,19 @@ function collectRelativeLeavesOnAndSpine(where, resolveField2) {
|
|
|
13226
13456
|
visit(where);
|
|
13227
13457
|
return failure === null ? { ok: true, leaves } : { ok: false, reasonCodes: [failure] };
|
|
13228
13458
|
}
|
|
13229
|
-
function
|
|
13230
|
-
|
|
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;
|
|
13231
13467
|
}
|
|
13232
|
-
function
|
|
13468
|
+
function collectServerFunctionOccurrences(where) {
|
|
13233
13469
|
const found = [];
|
|
13234
13470
|
const visitWhere = (node) => {
|
|
13235
|
-
if (node.type === "BINARY" &&
|
|
13471
|
+
if (node.type === "BINARY" && serverFunctionNameOf(node) !== null) {
|
|
13236
13472
|
found.push(node);
|
|
13237
13473
|
return;
|
|
13238
13474
|
}
|
|
@@ -13373,7 +13609,7 @@ function collectFieldMetadata(where, resolveField2) {
|
|
|
13373
13609
|
function serializedMultisetContains2(query, expectedNames) {
|
|
13374
13610
|
const expected = /* @__PURE__ */ new Map();
|
|
13375
13611
|
for (const name of expectedNames) {
|
|
13376
|
-
if (!
|
|
13612
|
+
if (!SERVER_ONLY_WHERE_FUNCTION_NAMES.has(name)) return false;
|
|
13377
13613
|
expected.set(name, (expected.get(name) ?? 0) + 1);
|
|
13378
13614
|
}
|
|
13379
13615
|
for (const [name, count] of expected) {
|
|
@@ -15527,7 +15763,7 @@ async function resolveSelectWhereCapability(stmt, client, cacheContext, material
|
|
|
15527
15763
|
}
|
|
15528
15764
|
function formatWhereCapabilityFailure(result) {
|
|
15529
15765
|
const reason = result.reasons.find(
|
|
15530
|
-
(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"
|
|
15531
15767
|
) ?? result.reasons[0];
|
|
15532
15768
|
const details = [
|
|
15533
15769
|
reason?.field ? `field=${reason.field}` : null,
|
|
@@ -15616,7 +15852,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
|
|
|
15616
15852
|
capability: whereCapability,
|
|
15617
15853
|
context: { allowFullScanExact: true },
|
|
15618
15854
|
serializedWholeWhere,
|
|
15619
|
-
relativeFunctionNames:
|
|
15855
|
+
relativeFunctionNames: serverOnlyFunctionOccurrencesInWhere(stmt.where)
|
|
15620
15856
|
}) ?? void 0;
|
|
15621
15857
|
if (fullScanExactPlan) prefilterPlan = fullScanExactPlan.prefilterPlan;
|
|
15622
15858
|
}
|
|
@@ -20698,7 +20934,7 @@ function renderResidualValue(node) {
|
|
|
20698
20934
|
case "CONCAT_OP":
|
|
20699
20935
|
return `(${renderResidualValue(value["left"])} ${typeof value["op"] === "string" ? value["op"] : value["type"] === "CONCAT_OP" ? "||" : "<op>"} ${renderResidualValue(value["right"])})`;
|
|
20700
20936
|
case "KINTONE_FUNC":
|
|
20701
|
-
return typeof value["name"] === "string" ? `${value["name"]}(...)` : "<expr>";
|
|
20937
|
+
return typeof value["name"] === "string" ? value["name"] === "LOGINUSER" ? "LOGINUSER()" : `${value["name"]}(...)` : "<expr>";
|
|
20702
20938
|
case "IN_LIST":
|
|
20703
20939
|
return Array.isArray(value["values"]) ? `(${value["values"].map(renderResidualValue).join(", ")})` : "<expr>";
|
|
20704
20940
|
case "ARRAY":
|
|
@@ -20734,10 +20970,11 @@ function renderRelativeDateResidualWhere(where) {
|
|
|
20734
20970
|
}
|
|
20735
20971
|
}
|
|
20736
20972
|
function relativeDateExplainLines(plan) {
|
|
20737
|
-
if (!plan.
|
|
20973
|
+
if (!plan.hasServerOnlyWhereFunction) return [];
|
|
20738
20974
|
if (!plan.allowed && plan.rejection) {
|
|
20975
|
+
const label = isRelativeDateFunctionName(plan.rejection.functionName) ? "relative date function" : "kintone function";
|
|
20739
20976
|
return [
|
|
20740
|
-
`
|
|
20977
|
+
` ${label}: ${plan.rejection.functionName}`,
|
|
20741
20978
|
" plan status: rejected",
|
|
20742
20979
|
` reason: ${plan.rejection.reasonCodes.join(", ")}`,
|
|
20743
20980
|
" client evaluation: forbidden",
|
|
@@ -20749,15 +20986,15 @@ function relativeDateExplainLines(plan) {
|
|
|
20749
20986
|
const fullScanExactPlan = node.fullScanExactPlan;
|
|
20750
20987
|
if (node.allowed && node.allowForm === "FULL_SCAN_EXACT" && fullScanExactPlan) {
|
|
20751
20988
|
for (const leaf of fullScanExactPlan.prefilterPlan.exactRelativeLeaves) {
|
|
20752
|
-
const functionName = leaf
|
|
20989
|
+
const functionName = serverFunctionNameOfExplainLeaf(leaf);
|
|
20753
20990
|
const field = leaf.left.type === "FIELD" ? leaf.left.field : void 0;
|
|
20754
20991
|
const operator = relativeReasonOperator(leaf.op);
|
|
20755
20992
|
const detail = node.capability?.reasons.find(
|
|
20756
20993
|
(reason) => reason.functionName === functionName && (field === void 0 || reason.field === field) && reason.operator === operator
|
|
20757
20994
|
);
|
|
20758
20995
|
lines.push(
|
|
20759
|
-
`
|
|
20760
|
-
|
|
20996
|
+
` ${serverFunctionLabel(functionName)}: ${functionName}`,
|
|
20997
|
+
` ${serverFunctionEvaluationLabel(functionName)}: kintone server whole-WHERE exact`,
|
|
20761
20998
|
` field: ${detail?.field ?? field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
20762
20999
|
` operator: ${detail?.operator ?? operator}`
|
|
20763
21000
|
);
|
|
@@ -20767,7 +21004,9 @@ function relativeDateExplainLines(plan) {
|
|
|
20767
21004
|
" where capability: EXACT_PUSHDOWN",
|
|
20768
21005
|
` server predicate: ${wholeWhereQuery}`,
|
|
20769
21006
|
" client residual: (none)",
|
|
20770
|
-
|
|
21007
|
+
` ${serverFunctionClientEvaluationLabel(
|
|
21008
|
+
fullScanExactPlan.prefilterPlan.exactRelativeLeaves
|
|
21009
|
+
)}: 0`,
|
|
20771
21010
|
` kintone query: ${wholeWhereQuery}`
|
|
20772
21011
|
);
|
|
20773
21012
|
continue;
|
|
@@ -20775,15 +21014,15 @@ function relativeDateExplainLines(plan) {
|
|
|
20775
21014
|
const prefilterPlan = node.prefilterPlan;
|
|
20776
21015
|
if (node.allowed && prefilterPlan?.prefilterWhere && prefilterPlan.residualWhere) {
|
|
20777
21016
|
for (const leaf of prefilterPlan.exactRelativeLeaves) {
|
|
20778
|
-
const functionName = leaf
|
|
21017
|
+
const functionName = serverFunctionNameOfExplainLeaf(leaf);
|
|
20779
21018
|
const field = leaf.left.type === "FIELD" ? leaf.left.field : void 0;
|
|
20780
21019
|
const operator = relativeReasonOperator(leaf.op);
|
|
20781
21020
|
const detail = node.capability?.reasons.find(
|
|
20782
21021
|
(reason) => reason.functionName === functionName && (field === void 0 || reason.field === field) && reason.operator === operator
|
|
20783
21022
|
);
|
|
20784
21023
|
lines.push(
|
|
20785
|
-
`
|
|
20786
|
-
|
|
21024
|
+
` ${serverFunctionLabel(functionName)}: ${functionName}`,
|
|
21025
|
+
` ${serverFunctionEvaluationLabel(functionName)}: kintone server exact prefilter`,
|
|
20787
21026
|
` field: ${detail?.field ?? field ?? "(unknown)"} (${detail?.fieldType ?? "unknown"})`,
|
|
20788
21027
|
` operator: ${detail?.operator ?? operator}`
|
|
20789
21028
|
);
|
|
@@ -20793,7 +21032,7 @@ function relativeDateExplainLines(plan) {
|
|
|
20793
21032
|
" where capability: SUPERSET_PREFILTER",
|
|
20794
21033
|
` server prefilter: ${serverPrefilter}`,
|
|
20795
21034
|
` client residual: ${renderRelativeDateResidualWhere(prefilterPlan.residualWhere)}`,
|
|
20796
|
-
|
|
21035
|
+
` ${serverFunctionClientEvaluationLabel(prefilterPlan.exactRelativeLeaves)}: 0`,
|
|
20797
21036
|
` kintone query: ${serverPrefilter}`
|
|
20798
21037
|
);
|
|
20799
21038
|
continue;
|
|
@@ -20802,6 +21041,19 @@ function relativeDateExplainLines(plan) {
|
|
|
20802
21041
|
const detail = node.capability?.reasons.find(
|
|
20803
21042
|
(reason) => reason.functionName === functionName
|
|
20804
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
|
+
}
|
|
20805
21057
|
lines.push(
|
|
20806
21058
|
` relative date function: ${functionName}`,
|
|
20807
21059
|
" evaluation: kintone server",
|
|
@@ -20815,6 +21067,24 @@ function relativeDateExplainLines(plan) {
|
|
|
20815
21067
|
}
|
|
20816
21068
|
return lines;
|
|
20817
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
|
+
}
|
|
20818
21088
|
async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4, cursorMaxActive = 2, enableImport = false, dmlMaxRows = 100, dmlMaxSubtableRows = DEFAULT_APPLY_MAX_SUBTABLE_ROWS) {
|
|
20819
21089
|
const statements = parseSqlBatch(sql, enableImport);
|
|
20820
21090
|
const analysis = analyzeBatch(statements);
|
|
@@ -20833,7 +21103,7 @@ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheConte
|
|
|
20833
21103
|
maxRecords,
|
|
20834
21104
|
relativeDatePlan
|
|
20835
21105
|
);
|
|
20836
|
-
const statementPlan = relativeDatePlan.
|
|
21106
|
+
const statementPlan = relativeDatePlan.hasServerOnlyWhereFunction && !relativeDatePlan.allowed ? relativeDateExplainLines(relativeDatePlan) : [
|
|
20837
21107
|
...relativeDateExplainLines(relativeDatePlan),
|
|
20838
21108
|
...addCursorConcurrency(buildBatchStatementPlan(
|
|
20839
21109
|
planStmt,
|
|
@@ -20962,7 +21232,7 @@ async function executeExplain(stmt, client, cacheContext, maxRecords, cursorMaxA
|
|
|
20962
21232
|
sharedPlan
|
|
20963
21233
|
);
|
|
20964
21234
|
const relativeLines = relativeDateExplainLines(sharedPlan);
|
|
20965
|
-
const lines = sharedPlan.
|
|
21235
|
+
const lines = sharedPlan.hasServerOnlyWhereFunction && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
|
|
20966
21236
|
...explainMetadataLines(analysis),
|
|
20967
21237
|
...relativeLines,
|
|
20968
21238
|
...addCursorConcurrency(
|