@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 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\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059";
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) => item.type === "STRING" ? convertString(item) : numberLiteralText(item)
5087
- ).join(",");
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 requireExactRelativeDatePushdown({
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, normalizeOperator(op));
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 === "TODAY" || value.name === "NOW" || value.name === "LOGINUSER");
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 requireExactRelativeDatePushdown({ capability: "UNSUPPORTED", reasons });
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 requireExactRelativeDatePushdown({
12701
+ return requireExactFunctionPushdown({
12566
12702
  capability: "SUPERSET_PREFILTER",
12567
12703
  reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
12568
12704
  });
12569
12705
  }
12570
- return requireExactRelativeDatePushdown({ capability: "LOCAL_ONLY", reasons });
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((reason) => reason.functionName !== void 0);
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((reason) => reason.functionName !== void 0);
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 = relativeDateFunctionOccurrencesInWhere(select.where);
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: collectExactRelativeLeaves(select.where),
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 relativeDateFunctionOccurrencesInWhere(where) {
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" && isRelativeDateFunctionName(value["name"])) {
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 collectExactRelativeLeaves(where) {
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" && isRelativeDateFunctionName(node.right.name)) {
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 (!isRelativeDateFunctionName(name)) return false;
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" && isRelativeDateFunctionName(value["name"]) && !seen.has(value["name"])) {
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: relativeDateFunctionOccurrencesInWhere(select.where)
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
- forceForbidden,
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, false);
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(cte.query, `${path}.cte[${index}]`, candidates, true, true, false);
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(statement.query, `${path}.main`, candidates, true, true, false);
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, true);
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, true, true, false);
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: relativeDateFunctionOccurrencesInWhere(statement.where)
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: relativeDateFunctionOccurrencesInWhere(statement.where)
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
- return names.every((name) => new RegExp(`\\b${name}\\s*\\(`).test(query));
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 relativeDateReasonCodes(capability) {
13143
+ function serverFunctionReasonCodes(functionName, capability) {
12939
13144
  const codes = (capability?.reasons ?? []).filter(
12940
- (reason) => reason.functionName !== void 0
12941
- ).map((reason) => reason.code).filter((code) => code.startsWith("WHERE_RELATIVE_DATE_"));
12942
- if (!codes.includes(WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN)) {
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 reasonCodes = relativeDateReasonCodes(capability);
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: candidate.functionNames[0],
13168
+ functionName,
12951
13169
  path: candidate.path,
12952
13170
  code: reasonCodes.find(
12953
- (code) => code !== WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN
12954
- ) ?? WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN,
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: true,
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(restQuery2, candidate.functionNames);
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: true,
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(restQuery, candidate.functionNames);
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: true,
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.length > 0,
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 !== WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN
13307
+ (code) => code !== requiresExact
13073
13308
  );
13074
13309
  throw new Error(
13075
- `${plan.rejection.functionName}: ${WHERE_RELATIVE_DATE_REQUIRES_EXACT_PUSHDOWN}${details.length > 0 ? ` (reason=${details.join(", ")})` : ""} (path=${plan.rejection.path})`
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 = collectRelativeOccurrences(stmt.where);
13331
+ const occurrences = collectServerFunctionOccurrences(stmt.where);
13097
13332
  if (occurrences.length === 0) return reject(["NO_RELATIVE_DATE"]);
13098
- const spine = collectRelativeLeavesOnAndSpine(stmt.where, resolveField2);
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 = relativeNameOf(leaf);
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) => relativeNameOf(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 && collectRelativeOccurrences(residualWhere).length > 0) {
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 collectRelativeLeavesOnAndSpine(where, resolveField2) {
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 (collectRelativeOccurrences(node).length > 0) {
13426
+ if (collectServerFunctionOccurrences(node).length > 0) {
13192
13427
  failure = "RELATIVE_DATE_CONTEXT_UNSUPPORTED";
13193
13428
  }
13194
13429
  return;
13195
13430
  case "NOT":
13196
- if (collectRelativeOccurrences(node).length > 0) {
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 = relativeNameOf(node);
13436
+ const name = serverFunctionNameOf(node);
13202
13437
  if (name === null) return;
13203
- const result = classifyRelativeDateBinary(
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 (collectRelativeOccurrences(node).length > 0) {
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 relativeNameOf(leaf) {
13230
- return leaf.right.type === "KINTONE_FUNC" && isRelativeDateFunctionName(leaf.right.name) ? leaf.right.name : null;
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 collectRelativeOccurrences(where) {
13468
+ function collectServerFunctionOccurrences(where) {
13233
13469
  const found = [];
13234
13470
  const visitWhere = (node) => {
13235
- if (node.type === "BINARY" && relativeNameOf(node) !== null) {
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 (!RELATIVE_DATE_FUNCTION_NAMES.has(name)) return false;
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: relativeDateFunctionOccurrencesInWhere(stmt.where)
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.hasRelativeDate) return [];
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
- ` relative date function: ${plan.rejection.functionName}`,
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.right.type === "KINTONE_FUNC" ? leaf.right.name : "(unknown)";
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
- ` relative date function: ${functionName}`,
20760
- " relative date evaluation: kintone server whole-WHERE exact",
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
- " relative date client evaluations: 0",
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.right.type === "KINTONE_FUNC" ? leaf.right.name : "(unknown)";
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
- ` relative date function: ${functionName}`,
20786
- " relative date evaluation: kintone server exact prefilter",
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
- " relative date client evaluations: 0",
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.hasRelativeDate && !relativeDatePlan.allowed ? relativeDateExplainLines(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.hasRelativeDate && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
21235
+ const lines = sharedPlan.hasServerOnlyWhereFunction && !sharedPlan.allowed ? [...explainMetadataLines(analysis), ...relativeLines] : [
20966
21236
  ...explainMetadataLines(analysis),
20967
21237
  ...relativeLines,
20968
21238
  ...addCursorConcurrency(