@rex0220/kintone-sql-tools 2.8.0 → 2.10.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.
@@ -31362,6 +31362,9 @@ function isJapanese(cp) {
31362
31362
  return cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
31363
31363
  }
31364
31364
 
31365
+ // src/types/ast.ts
31366
+ var NO_FROM_CTE_NAME = "__NO_FROM__";
31367
+
31365
31368
  // src/parser/parser.ts
31366
31369
  var MAX_BATCH_STATEMENTS = 20;
31367
31370
  var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
@@ -31875,7 +31878,7 @@ var Parser = class {
31875
31878
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
31876
31879
  const columns = this.parseSelectColumns();
31877
31880
  const hasFrom = this.consume("FROM" /* FROM */);
31878
- const from = hasFrom ? this.parseTableRef() : { appId: 0, alias: null, cteName: "__NO_FROM__" };
31881
+ const from = hasFrom ? this.parseTableRef() : { appId: 0, alias: null, cteName: NO_FROM_CTE_NAME };
31879
31882
  const joins = hasFrom ? this.parseJoins() : [];
31880
31883
  const where = this.consume("WHERE" /* WHERE */) ? this.parseWhereExpr() : null;
31881
31884
  let groupBy = [];
@@ -33263,7 +33266,7 @@ function hasWhereClause(stmt) {
33263
33266
  function isNoFromSelectStatement(stmt) {
33264
33267
  if (!stmt || typeof stmt !== "object") return false;
33265
33268
  const obj = stmt;
33266
- return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName === "__NO_FROM__";
33269
+ return obj.type === "SELECT" && obj.from?.appId === 0 && obj.from?.cteName === NO_FROM_CTE_NAME;
33267
33270
  }
33268
33271
  function getInsertValuesCount(stmt) {
33269
33272
  if (!stmt || typeof stmt !== "object") return null;
@@ -34017,6 +34020,235 @@ function isAggregateSyntheticName(name) {
34017
34020
  return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
34018
34021
  }
34019
34022
 
34023
+ // src/core/cteInlining.ts
34024
+ function canInlineSingleCte(stmt) {
34025
+ if (stmt.ctes.length !== 1) return false;
34026
+ const cteDef = stmt.ctes[0];
34027
+ if (cteDef.query.type !== "SELECT" || resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
34028
+ const finalQuery = stmt.query;
34029
+ if (finalQuery.type !== "SELECT") return false;
34030
+ if (finalQuery.from.cteName !== cteDef.name || finalQuery.joins.length > 0) return false;
34031
+ if (finalQuery.groupBy.length > 0 || finalQuery.distinct) return false;
34032
+ return !finalQuery.columns.some(
34033
+ (column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
34034
+ );
34035
+ }
34036
+ function buildInlinedQuery(stmt) {
34037
+ const cteBody = stmt.ctes[0].query;
34038
+ const final = stmt.query;
34039
+ const finalWhere = stripCteAlias(final.where, final.from.alias);
34040
+ const where = cteBody.where === null ? finalWhere : finalWhere === null ? cteBody.where : { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
34041
+ const columns = final.columns.every((column) => column.type === "WILDCARD") ? cteBody.columns : final.columns;
34042
+ return {
34043
+ type: "SELECT",
34044
+ from: cteBody.from,
34045
+ joins: [],
34046
+ columns,
34047
+ where,
34048
+ groupBy: [],
34049
+ having: null,
34050
+ orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
34051
+ limit: final.limit ?? cteBody.limit,
34052
+ offset: final.offset ?? cteBody.offset,
34053
+ distinct: false
34054
+ };
34055
+ }
34056
+ function stripCteAlias(where, alias) {
34057
+ if (where === null || alias === null) return where;
34058
+ switch (where.type) {
34059
+ case "BINARY":
34060
+ return { ...where, left: stripCteAliasFromFieldValue(where.left, alias) };
34061
+ case "NULL_CHECK":
34062
+ return { ...where, field: stripCteAliasFromFieldValue(where.field, alias) };
34063
+ case "LOGICAL":
34064
+ return {
34065
+ ...where,
34066
+ left: stripCteAlias(where.left, alias),
34067
+ right: stripCteAlias(where.right, alias)
34068
+ };
34069
+ case "NOT":
34070
+ case "GROUP":
34071
+ return { ...where, expr: stripCteAlias(where.expr, alias) };
34072
+ case "EXISTS":
34073
+ return where;
34074
+ }
34075
+ }
34076
+ function stripCteAliasFromFieldValue(value, alias) {
34077
+ if (value.type === "FIELD" && value.tableAlias === alias) {
34078
+ return { ...value, tableAlias: null };
34079
+ }
34080
+ return value;
34081
+ }
34082
+
34083
+ // src/core/optimization/wherePredicatePushdown.ts
34084
+ function extractSafePushdownLeaves(where, options = {}) {
34085
+ return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
34086
+ }
34087
+ function extractTypedPushdownCandidates(where, options = {}) {
34088
+ return extractAndLeaves(
34089
+ where,
34090
+ (expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
34091
+ );
34092
+ }
34093
+ function extractAndLeaves(where, accept) {
34094
+ switch (where.type) {
34095
+ case "BINARY":
34096
+ return accept(where) ? where : null;
34097
+ case "LOGICAL":
34098
+ if (where.op !== "AND") return null;
34099
+ {
34100
+ const left = extractAndLeaves(where.left, accept);
34101
+ const right = extractAndLeaves(where.right, accept);
34102
+ if (left && right) return { ...where, left, right };
34103
+ return left ?? right ?? null;
34104
+ }
34105
+ case "GROUP":
34106
+ return extractAndLeaves(where.expr, accept);
34107
+ case "NULL_CHECK":
34108
+ case "NOT":
34109
+ case "EXISTS":
34110
+ return null;
34111
+ }
34112
+ }
34113
+ function isSafeComparison(expr, options) {
34114
+ if (isKlikeComparison(expr, options)) return true;
34115
+ if (isSafeIdComparison(expr, options)) return true;
34116
+ if (isNumericCandidate(expr, options)) {
34117
+ return options.fieldTypes?.get(expr.left.field) === "NUMBER";
34118
+ }
34119
+ return isSelectionInComparison(expr, options);
34120
+ }
34121
+ function isKlikeComparison(expr, options) {
34122
+ if (options.allowKlike === false) return false;
34123
+ if (expr.op !== "KLIKE" && expr.op !== "NOT_KLIKE") return false;
34124
+ if (expr.left.type !== "FIELD" || !isTargetField(expr.left, options)) return false;
34125
+ return expr.right.type === "STRING" || options.allowUnresolvedKlikeVariables === true && expr.right.type === "VARIABLE";
34126
+ }
34127
+ var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
34128
+ "DROP_DOWN",
34129
+ "RADIO_BUTTON",
34130
+ "CHECK_BOX",
34131
+ "MULTI_SELECT",
34132
+ "STATUS"
34133
+ ]);
34134
+ function isSelectionInComparison(expr, options) {
34135
+ if (!isSelectionInCandidate(expr, options)) return false;
34136
+ if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
34137
+ const fieldType = options.fieldTypes?.get(expr.left.field);
34138
+ if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
34139
+ const validOptions = options.fieldOptions?.get(expr.left.field);
34140
+ if (validOptions === void 0) return false;
34141
+ return expr.right.values.every(
34142
+ (value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
34143
+ );
34144
+ }
34145
+ function isSafeIdComparison(expr, options) {
34146
+ if (!isTargetIdField(expr.left, options)) return false;
34147
+ if (expr.right.type !== "NUMBER") return false;
34148
+ return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
34149
+ }
34150
+ function isTargetIdField(field, options) {
34151
+ if (field.type !== "FIELD" || field.field !== "$id") return false;
34152
+ const targetAlias = options.tableAlias ?? null;
34153
+ if (field.tableAlias === targetAlias) return true;
34154
+ return options.allowUnqualifiedFields === true && field.tableAlias === null;
34155
+ }
34156
+ function isNumericCandidate(expr, options) {
34157
+ if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
34158
+ if (!isTargetField(expr.left, options)) return false;
34159
+ if (expr.right.type !== "NUMBER") return false;
34160
+ if (expr.op === "=") return true;
34161
+ return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
34162
+ }
34163
+ function isSelectionInCandidate(expr, options) {
34164
+ if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
34165
+ if (!isTargetField(expr.left, options)) return false;
34166
+ if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
34167
+ if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
34168
+ return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
34169
+ }
34170
+ function isTargetField(field, options) {
34171
+ const targetAlias = options.tableAlias ?? null;
34172
+ if (field.tableAlias === targetAlias) return true;
34173
+ return options.allowUnqualifiedFields === true && field.tableAlias === null;
34174
+ }
34175
+
34176
+ // src/core/optimization/klikePushdownPlan.ts
34177
+ function buildKlikePushdownPlan(stmt, options = {}) {
34178
+ const joinsAreSafeForKlike = stmt.joins.every((join) => join.type === "INNER");
34179
+ const common = {
34180
+ allowKlike: joinsAreSafeForKlike,
34181
+ allowUnresolvedKlikeVariables: options.allowUnresolvedVariables
34182
+ };
34183
+ let mainCondition = null;
34184
+ if (stmt.where !== null && !stmt.from.subtableCode && stmt.from.cteName === null) {
34185
+ if (stmt.joins.length === 0) {
34186
+ mainCondition = extractSafePushdownLeaves(stmt.where, {
34187
+ ...common,
34188
+ tableAlias: stmt.from.alias ?? void 0,
34189
+ allowUnqualifiedFields: true,
34190
+ fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
34191
+ fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
34192
+ });
34193
+ } else if (stmt.from.alias) {
34194
+ mainCondition = extractSafePushdownLeaves(stmt.where, {
34195
+ ...common,
34196
+ tableAlias: stmt.from.alias,
34197
+ fieldTypes: options.fieldTypesByApp?.get(stmt.from.appId),
34198
+ fieldOptions: options.fieldOptionsByApp?.get(stmt.from.appId)
34199
+ });
34200
+ }
34201
+ }
34202
+ const joinConditions = /* @__PURE__ */ new Map();
34203
+ if (stmt.where !== null) {
34204
+ for (const join of stmt.joins) {
34205
+ if (!join.table.alias || join.table.subtableCode || join.table.cteName !== null) continue;
34206
+ const condition = extractSafePushdownLeaves(stmt.where, {
34207
+ ...common,
34208
+ tableAlias: join.table.alias,
34209
+ fieldTypes: options.fieldTypesByApp?.get(join.table.appId),
34210
+ fieldOptions: options.fieldOptionsByApp?.get(join.table.appId)
34211
+ });
34212
+ if (condition !== null) joinConditions.set(join.table.alias, condition);
34213
+ }
34214
+ }
34215
+ const appliedKlikes = /* @__PURE__ */ new Set();
34216
+ collectKlikes(mainCondition, appliedKlikes);
34217
+ for (const condition of joinConditions.values()) collectKlikes(condition, appliedKlikes);
34218
+ const allKlikes = /* @__PURE__ */ new Set();
34219
+ collectKlikes(stmt.where, allKlikes);
34220
+ return {
34221
+ mainCondition,
34222
+ joinConditions,
34223
+ appliedKlikes,
34224
+ allKlikes: [...allKlikes]
34225
+ };
34226
+ }
34227
+ function unappliedKlikes(plan) {
34228
+ return plan.allKlikes.filter((expr) => !plan.appliedKlikes.has(expr));
34229
+ }
34230
+ function collectKlikes(where, out) {
34231
+ if (where === null) return;
34232
+ if (isKlike(where)) {
34233
+ out.add(where);
34234
+ return;
34235
+ }
34236
+ switch (where.type) {
34237
+ case "LOGICAL":
34238
+ collectKlikes(where.left, out);
34239
+ collectKlikes(where.right, out);
34240
+ return;
34241
+ case "NOT":
34242
+ case "GROUP":
34243
+ collectKlikes(where.expr, out);
34244
+ return;
34245
+ case "BINARY":
34246
+ case "NULL_CHECK":
34247
+ case "EXISTS":
34248
+ return;
34249
+ }
34250
+ }
34251
+
34020
34252
  // src/core/klikeValidation.ts
34021
34253
  var KlikeValidationError = class extends Error {
34022
34254
  constructor(message) {
@@ -34027,6 +34259,13 @@ var KlikeValidationError = class extends Error {
34027
34259
  function validateKlikeStatement(stmt) {
34028
34260
  validateStatement(stmt);
34029
34261
  }
34262
+ function validateKlikePushdownPlan(plan) {
34263
+ if (unappliedKlikes(plan).length > 0) {
34264
+ throw new KlikeValidationError(
34265
+ "FULL_SCAN \u306E KLIKE / NOT KLIKE \u3092\u5B89\u5168\u306B\u62BC\u3057\u4E0B\u3052\u3089\u308C\u307E\u305B\u3093\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
34266
+ );
34267
+ }
34268
+ }
34030
34269
  function validateStatement(stmt) {
34031
34270
  switch (stmt.type) {
34032
34271
  case "SELECT":
@@ -34058,7 +34297,7 @@ function validateStatement(stmt) {
34058
34297
  case "REORDER":
34059
34298
  if (containsKlike(stmt)) {
34060
34299
  throw new KlikeValidationError(
34061
- "KLIKE / NOT KLIKE \u306F v1 \u3067\u306F\u5168 DML\uFF08UPDATE / DELETE / INSERT / UPSERT / REORDER\uFF09\u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
34300
+ "KLIKE / NOT KLIKE \u306F\u5168 DML\uFF08UPDATE / DELETE / INSERT / UPSERT / REORDER\uFF09\u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
34062
34301
  );
34063
34302
  }
34064
34303
  return;
@@ -34078,9 +34317,8 @@ function validateUnion(stmt) {
34078
34317
  validateSelect(stmt.right);
34079
34318
  }
34080
34319
  function validateWith(stmt) {
34081
- const inlined = buildEffectiveInlineSelect(stmt);
34082
- if (inlined !== null) {
34083
- validateSelect(inlined);
34320
+ if (canInlineSingleCte(stmt)) {
34321
+ validateSelect(buildInlinedQuery(stmt));
34084
34322
  return;
34085
34323
  }
34086
34324
  for (const cte of stmt.ctes) {
@@ -34092,10 +34330,15 @@ function validateWith(stmt) {
34092
34330
  function validateSelect(stmt) {
34093
34331
  validateOwnKlikeExpressions(stmt);
34094
34332
  if (whereHasKlike(stmt.where)) {
34095
- const inMemorySource = stmt.from.cteName !== null || stmt.joins.some((join) => join.table.cteName !== null);
34096
- if (inMemorySource || resolveSelectMode(stmt) === "FULL_SCAN") {
34333
+ const directKintoneSimple = resolveSelectMode(stmt) === "SIMPLE" && stmt.from.cteName === null && stmt.joins.every((join) => join.table.cteName === null);
34334
+ if (!directKintoneSimple) {
34335
+ const plan = buildKlikePushdownPlan(stmt, { allowUnresolvedVariables: true });
34336
+ if (unappliedKlikes(plan).length === 0) {
34337
+ validateNestedSelects(stmt);
34338
+ return;
34339
+ }
34097
34340
  throw new KlikeValidationError(
34098
- "KLIKE / NOT KLIKE \u306F kintone \u3078\u62BC\u3057\u4E0B\u3052\u308B SIMPLE SELECT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\u3053\u306E SELECT \u306F FULL_SCAN \u306B\u306A\u308A\u307E\u3059"
34341
+ "FULL_SCAN \u306E KLIKE / NOT KLIKE \u306F\u3001\u7269\u7406\u30C6\u30FC\u30D6\u30EB\u306B\u5BFE\u3059\u308B AND \u30EA\u30FC\u30D5\u3068\u3057\u3066\u5FC5\u305A\u62BC\u3057\u4E0B\u3052\u3089\u308C\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002OR / NOT \u914D\u4E0B\u3001CTE\u30FB\u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3001LEFT / RIGHT JOIN \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
34099
34342
  );
34100
34343
  }
34101
34344
  }
@@ -34125,33 +34368,6 @@ function validateNestedSelects(node) {
34125
34368
  if (obj.type === "SELECT") validateSelect(obj);
34126
34369
  }, true);
34127
34370
  }
34128
- function buildEffectiveInlineSelect(stmt) {
34129
- if (stmt.ctes.length !== 1) return null;
34130
- const cte = stmt.ctes[0];
34131
- if (cte.query.type !== "SELECT" || resolveSelectMode(cte.query) !== "SIMPLE") return null;
34132
- if (stmt.query.type !== "SELECT") return null;
34133
- const final = stmt.query;
34134
- if (final.from.cteName !== cte.name || final.joins.length > 0) return null;
34135
- if (final.groupBy.length > 0 || final.distinct) return null;
34136
- if (final.columns.some(
34137
- (column) => column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL"
34138
- )) return null;
34139
- const where = cte.query.where === null ? final.where : final.where === null ? cte.query.where : { type: "LOGICAL", op: "AND", left: cte.query.where, right: final.where };
34140
- const columns = final.columns.every((column) => column.type === "WILDCARD") ? cte.query.columns : final.columns;
34141
- return {
34142
- type: "SELECT",
34143
- from: cte.query.from,
34144
- joins: [],
34145
- columns,
34146
- where,
34147
- groupBy: [],
34148
- having: null,
34149
- orderBy: final.orderBy.length > 0 ? final.orderBy : cte.query.orderBy,
34150
- limit: final.limit ?? cte.query.limit,
34151
- offset: final.offset ?? cte.query.offset,
34152
- distinct: false
34153
- };
34154
- }
34155
34371
  function containsKlike(node) {
34156
34372
  let found = false;
34157
34373
  walkObjects(node, (obj) => {
@@ -34656,25 +34872,29 @@ function resolveFieldRef(row, field) {
34656
34872
  }
34657
34873
 
34658
34874
  // src/engine/evalWhere.ts
34659
- function evalWhere(expr, row, resolveFieldType) {
34875
+ function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
34660
34876
  switch (expr.type) {
34661
34877
  case "BINARY":
34662
- return evalBinary(expr, row, resolveFieldType);
34878
+ return evalBinary(expr, row, resolveFieldType, appliedKlikes);
34663
34879
  case "NULL_CHECK":
34664
34880
  return evalNullCheck(expr, row);
34665
34881
  case "LOGICAL":
34666
- return evalLogical(expr, row, resolveFieldType);
34882
+ return evalLogical(expr, row, resolveFieldType, appliedKlikes);
34667
34883
  case "NOT":
34668
- return !evalWhere(expr.expr, row, resolveFieldType);
34884
+ return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
34669
34885
  case "GROUP":
34670
- return evalWhere(expr.expr, row, resolveFieldType);
34886
+ return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
34671
34887
  case "EXISTS": {
34672
34888
  const exists = expr.resolved;
34673
34889
  return expr.not ? !exists : exists;
34674
34890
  }
34675
34891
  }
34676
34892
  }
34677
- function evalBinary(expr, row, resolveFieldType) {
34893
+ function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
34894
+ if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
34895
+ if (appliedKlikes?.has(expr)) return true;
34896
+ throw new Error("KLIKE / NOT KLIKE \u306F\u62BC\u3057\u4E0B\u3052\u6E08\u307F\u96C6\u5408\u306B\u542B\u307E\u308C\u306A\u3044\u305F\u3081 JavaScript \u5074\u3067\u306F\u8A55\u4FA1\u3067\u304D\u307E\u305B\u3093");
34897
+ }
34678
34898
  const left = resolveField(expr.left, row, resolveFieldType);
34679
34899
  const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
34680
34900
  return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
@@ -34756,11 +34976,11 @@ function evalNullCheck(expr, row) {
34756
34976
  const val = resolveField(expr.field, row);
34757
34977
  return expr.not ? val !== "" : val === "";
34758
34978
  }
34759
- function evalLogical(expr, row, resolveFieldType) {
34979
+ function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
34760
34980
  if (expr.op === "AND") {
34761
- return evalWhere(expr.left, row, resolveFieldType) && evalWhere(expr.right, row, resolveFieldType);
34981
+ return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
34762
34982
  }
34763
- return evalWhere(expr.left, row, resolveFieldType) || evalWhere(expr.right, row, resolveFieldType);
34983
+ return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
34764
34984
  }
34765
34985
  function resolveField(field, row, resolveFieldType) {
34766
34986
  if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
@@ -34861,7 +35081,7 @@ function matchLike(value, pattern) {
34861
35081
  function assertDmlWhereIsSafe(where) {
34862
35082
  if (whereHasKlike(where)) {
34863
35083
  throw new DmlConvertError(
34864
- "UPDATE / DELETE \u306E WHERE \u306B KLIKE / NOT KLIKE \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002kintone \u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22\u306E\u6253\u3061\u5207\u308A\u3092\u691C\u51FA\u3067\u304D\u306A\u3044\u305F\u3081\u3001v1 \u3067\u306F\u5168 DML \u3067\u5B89\u5168\u4E0A\u62D2\u5426\u3057\u307E\u3057\u305F\u3002"
35084
+ "UPDATE / DELETE \u306E WHERE \u306B KLIKE / NOT KLIKE \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002kintone \u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22\u306E\u6253\u3061\u5207\u308A\u3092\u691C\u51FA\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u5168 DML \u3067\u5B89\u5168\u4E0A\u62D2\u5426\u3057\u3066\u3044\u307E\u3059\u3002"
34865
35085
  );
34866
35086
  }
34867
35087
  if (!whereHasLike(where)) return;
@@ -35203,6 +35423,7 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35203
35423
  let windowOffset = 0;
35204
35424
  const cursorQuery0 = buildCursorQuery(query, cursorId);
35205
35425
  const first = await fetchPage(fetcher, app, cursorQuery0, fetchFields, pageSize, windowOffset);
35426
+ notifySearchAborted(first, options);
35206
35427
  allRecords.push(...first.records);
35207
35428
  if (allRecords.length > maxRecords2) {
35208
35429
  if (onLimit2 === "truncate") {
@@ -35246,6 +35467,7 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35246
35467
  (offset) => fetchPage(fetcher, app, cq, fetchFields, pageSize, offset)
35247
35468
  )
35248
35469
  );
35470
+ for (const response of responses) notifySearchAborted(response, options);
35249
35471
  let done = false;
35250
35472
  for (const res of responses) {
35251
35473
  allRecords.push(...res.records);
@@ -35274,6 +35496,9 @@ async function fetchAll(fetcher, app, query, fields, options = {}) {
35274
35496
  }
35275
35497
  return allRecords;
35276
35498
  }
35499
+ function notifySearchAborted(response, options) {
35500
+ if (response.searchAborted) options.onSearchAborted?.();
35501
+ }
35277
35502
  function extractIds(records) {
35278
35503
  return records.map((r) => {
35279
35504
  const raw = r["$id"]?.value;
@@ -35330,7 +35555,8 @@ async function fetchRecordsForSharedPlan(getRecords, app, query, fields, options
35330
35555
  maxRecords: options.maxRecords,
35331
35556
  parallel: options.parallel,
35332
35557
  onLimit: options.onLimit ?? "error",
35333
- onTruncate: options.onTruncate
35558
+ onTruncate: options.onTruncate,
35559
+ onSearchAborted: options.onSearchAborted
35334
35560
  });
35335
35561
  return {
35336
35562
  records,
@@ -35351,92 +35577,6 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
35351
35577
  };
35352
35578
  }
35353
35579
 
35354
- // src/core/optimization/wherePredicatePushdown.ts
35355
- function extractSafePushdownLeaves(where, options = {}) {
35356
- return extractAndLeaves(where, (expr) => isSafeComparison(expr, options));
35357
- }
35358
- function extractTypedPushdownCandidates(where, options = {}) {
35359
- return extractAndLeaves(
35360
- where,
35361
- (expr) => isNumericCandidate(expr, options) || isSelectionInCandidate(expr, options)
35362
- );
35363
- }
35364
- function extractAndLeaves(where, accept) {
35365
- switch (where.type) {
35366
- case "BINARY":
35367
- return accept(where) ? where : null;
35368
- case "LOGICAL":
35369
- if (where.op !== "AND") return null;
35370
- {
35371
- const left = extractAndLeaves(where.left, accept);
35372
- const right = extractAndLeaves(where.right, accept);
35373
- if (left && right) return { ...where, left, right };
35374
- return left ?? right ?? null;
35375
- }
35376
- case "GROUP":
35377
- return extractAndLeaves(where.expr, accept);
35378
- case "NULL_CHECK":
35379
- case "NOT":
35380
- case "EXISTS":
35381
- return null;
35382
- }
35383
- }
35384
- function isSafeComparison(expr, options) {
35385
- if (isSafeIdComparison(expr, options)) return true;
35386
- if (isNumericCandidate(expr, options)) {
35387
- return options.fieldTypes?.get(expr.left.field) === "NUMBER";
35388
- }
35389
- return isSelectionInComparison(expr, options);
35390
- }
35391
- var SELECTION_IN_FIELD_TYPES = /* @__PURE__ */ new Set([
35392
- "DROP_DOWN",
35393
- "RADIO_BUTTON",
35394
- "CHECK_BOX",
35395
- "MULTI_SELECT",
35396
- "STATUS"
35397
- ]);
35398
- function isSelectionInComparison(expr, options) {
35399
- if (!isSelectionInCandidate(expr, options)) return false;
35400
- if (expr.left.type !== "FIELD" || expr.right.type !== "IN_LIST") return false;
35401
- const fieldType = options.fieldTypes?.get(expr.left.field);
35402
- if (fieldType === void 0 || !SELECTION_IN_FIELD_TYPES.has(fieldType)) return false;
35403
- const validOptions = options.fieldOptions?.get(expr.left.field);
35404
- if (validOptions === void 0) return false;
35405
- return expr.right.values.every(
35406
- (value) => value.type === "STRING" && value.value !== "" && validOptions.has(value.value)
35407
- );
35408
- }
35409
- function isSafeIdComparison(expr, options) {
35410
- if (!isTargetIdField(expr.left, options)) return false;
35411
- if (expr.right.type !== "NUMBER") return false;
35412
- return expr.op === "=" || expr.op === ">" || expr.op === "<" || expr.op === ">=" || expr.op === "<=";
35413
- }
35414
- function isTargetIdField(field, options) {
35415
- if (field.type !== "FIELD" || field.field !== "$id") return false;
35416
- const targetAlias = options.tableAlias ?? null;
35417
- if (field.tableAlias === targetAlias) return true;
35418
- return options.allowUnqualifiedFields === true && field.tableAlias === null;
35419
- }
35420
- function isNumericCandidate(expr, options) {
35421
- if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
35422
- if (!isTargetField(expr.left, options)) return false;
35423
- if (expr.right.type !== "NUMBER") return false;
35424
- if (expr.op === "=") return true;
35425
- return (expr.op === "<" || expr.op === ">") && Number.isSafeInteger(expr.right.value);
35426
- }
35427
- function isSelectionInCandidate(expr, options) {
35428
- if (expr.left.type !== "FIELD" || expr.left.field === "$id") return false;
35429
- if (!isTargetField(expr.left, options)) return false;
35430
- if (expr.op !== "IN" && expr.op !== "NOT_IN") return false;
35431
- if (expr.right.type !== "IN_LIST" || expr.right.values.length === 0) return false;
35432
- return expr.right.values.every((value) => value.type === "STRING" && value.value !== "");
35433
- }
35434
- function isTargetField(field, options) {
35435
- const targetAlias = options.tableAlias ?? null;
35436
- if (field.tableAlias === targetAlias) return true;
35437
- return options.allowUnqualifiedFields === true && field.tableAlias === null;
35438
- }
35439
-
35440
35580
  // src/engine/process.ts
35441
35581
  function flatten(record2, alias) {
35442
35582
  const row = {};
@@ -35501,9 +35641,9 @@ function applyJoin(leftRows, rightRows, join) {
35501
35641
  }
35502
35642
  return result;
35503
35643
  }
35504
- function applyFilter(rows, where, resolveFieldType) {
35644
+ function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
35505
35645
  if (where === null) return rows;
35506
- return rows.filter((row) => evalWhere(where, row, resolveFieldType));
35646
+ return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
35507
35647
  }
35508
35648
  function hasAggregateColumns(columns) {
35509
35649
  return columns.some(
@@ -35960,7 +36100,8 @@ function runFullScan(input) {
35960
36100
  optionOrders,
35961
36101
  sortKinds,
35962
36102
  fieldTypeResolver,
35963
- havingFieldTypeResolver
36103
+ havingFieldTypeResolver,
36104
+ appliedKlikes
35964
36105
  } = input;
35965
36106
  let rows = [];
35966
36107
  const mainAlias = stmt.from.alias;
@@ -35972,7 +36113,7 @@ function runFullScan(input) {
35972
36113
  const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
35973
36114
  rows = applyJoin(rows, rightRows, join);
35974
36115
  }
35975
- rows = applyFilter(rows, stmt.where, fieldTypeResolver);
36116
+ rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
35976
36117
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
35977
36118
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
35978
36119
  }
@@ -36028,13 +36169,32 @@ function toFlatString(value) {
36028
36169
  }
36029
36170
 
36030
36171
  // src/execute.ts
36172
+ var SEARCH_ABORTED_WARNING = "\u691C\u7D22\u304C 10 \u4E07\u4EF6\u3067\u6253\u3061\u5207\u3089\u308C\u3001\u7D50\u679C\u304C\u6B20\u843D\u3057\u305F\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002";
36173
+ var SearchAbortedError = class extends Error {
36174
+ constructor() {
36175
+ super("SearchAbortedError: kintone \u306E\u691C\u7D22\u304C 10 \u4E07\u4EF6\u3067\u6253\u3061\u5207\u3089\u308C\u305F\u305F\u3081\u3001\u5B8C\u5168\u306A\u5BFE\u8C61\u96C6\u5408\u3092\u78BA\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002");
36176
+ this.name = "SearchAbortedError";
36177
+ }
36178
+ };
36031
36179
  async function execute(sql, client, options = {}) {
36180
+ const startedAt = Date.now();
36181
+ const stmt = parseSql(sql);
36032
36182
  const metrics = createEmptyMetrics();
36033
36183
  const countedClient = wrapClientWithMetrics(client, metrics);
36034
- const startedAt = Date.now();
36035
- const result = await executeStatement(sql, countedClient, options);
36184
+ const collector = { aborted: false };
36185
+ const guardedClient = wrapClientWithSearchAbort(
36186
+ countedClient,
36187
+ collector,
36188
+ !isSelectLikeStatement(stmt)
36189
+ );
36190
+ const result = await executeParsedStatement(
36191
+ stmt,
36192
+ guardedClient,
36193
+ options,
36194
+ options.cacheContext ?? "default"
36195
+ );
36036
36196
  metrics.elapsedMs = Date.now() - startedAt;
36037
- return { ...result, metrics };
36197
+ return { ...attachSearchAbortWarning(result, collector), metrics };
36038
36198
  }
36039
36199
  function createEmptyMetrics() {
36040
36200
  return {
@@ -36083,10 +36243,27 @@ function wrapClientWithMetrics(client, metrics) {
36083
36243
  }
36084
36244
  };
36085
36245
  }
36086
- async function executeStatement(sql, client, options) {
36087
- const cacheContext = options.cacheContext ?? "default";
36088
- const stmt = parseSql(sql);
36089
- return executeParsedStatement(stmt, client, options, cacheContext);
36246
+ function wrapClientWithSearchAbort(client, collector, failClosed) {
36247
+ return {
36248
+ ...client,
36249
+ getRecords: async (params) => {
36250
+ const response = await client.getRecords(params);
36251
+ if (response.searchAborted) {
36252
+ collector.aborted = true;
36253
+ if (failClosed) throw new SearchAbortedError();
36254
+ }
36255
+ return response;
36256
+ }
36257
+ };
36258
+ }
36259
+ function isSelectLikeStatement(stmt) {
36260
+ return stmt.type === "SELECT" || stmt.type === "UNION" || stmt.type === "WITH";
36261
+ }
36262
+ function attachSearchAbortWarning(result, collector) {
36263
+ if (!collector.aborted || result.type !== "SELECT") return result;
36264
+ const warnings = new Set(result.warnings ?? []);
36265
+ warnings.add(SEARCH_ABORTED_WARNING);
36266
+ return { ...result, warnings: [...warnings] };
36090
36267
  }
36091
36268
  async function executeParsedStatement(stmt, client, options, cacheContext) {
36092
36269
  const unresolved = findVariableRef(stmt);
@@ -36199,10 +36376,19 @@ async function executeBatch(sql, client, options = {}) {
36199
36376
  targetAppId: info.targetAppId
36200
36377
  })
36201
36378
  } : batchOptions;
36379
+ const searchAbortCollector = { aborted: false };
36380
+ const statementClient = wrapClientWithSearchAbort(
36381
+ countedClient,
36382
+ searchAbortCollector,
36383
+ info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH"
36384
+ );
36202
36385
  const outcome = await runWithDeadline(
36203
- executeBatchStatement(statements[i], info, countedClient, stmtOptions, cacheContext, tempTables, variables),
36386
+ executeBatchStatement(statements[i], info, statementClient, stmtOptions, cacheContext, tempTables, variables),
36204
36387
  remaining
36205
36388
  );
36389
+ if (outcome.result) {
36390
+ outcome.result = attachSearchAbortWarning(outcome.result, searchAbortCollector);
36391
+ }
36206
36392
  results.push({ ...base, status: "success", ...outcome });
36207
36393
  } catch (e) {
36208
36394
  results.push({ ...base, status: "error", error: toBatchStatementError(e) });
@@ -36533,7 +36719,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache) {
36533
36719
  }
36534
36720
  }
36535
36721
  function isNoFromSelect(stmt) {
36536
- return stmt.from.appId === 0 && stmt.from.cteName === "__NO_FROM__";
36722
+ return stmt.from.appId === 0 && stmt.from.cteName === NO_FROM_CTE_NAME;
36537
36723
  }
36538
36724
  function arithHasFieldRef(node) {
36539
36725
  if (node.type === "FIELD_REF") return true;
@@ -36660,23 +36846,6 @@ async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
36660
36846
  }
36661
36847
  }
36662
36848
  }
36663
- function extractMainSafePushdown(stmt, fieldTypes, fieldOptions) {
36664
- if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
36665
- if (stmt.joins.length === 0) {
36666
- return extractSafePushdownLeaves(stmt.where, {
36667
- tableAlias: stmt.from.alias ?? void 0,
36668
- allowUnqualifiedFields: true,
36669
- fieldTypes,
36670
- fieldOptions
36671
- });
36672
- }
36673
- if (!stmt.from.alias) return null;
36674
- return extractSafePushdownLeaves(stmt.where, {
36675
- tableAlias: stmt.from.alias,
36676
- fieldTypes,
36677
- fieldOptions
36678
- });
36679
- }
36680
36849
  function extractMainTypedPushdownCandidate(stmt) {
36681
36850
  if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
36682
36851
  if (stmt.joins.length === 0) {
@@ -36858,23 +37027,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
36858
37027
  loadTypedInFieldTypes(stmt, client, cacheContext)
36859
37028
  ]);
36860
37029
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
36861
- const mainPushDown = extractMainSafePushdown(
36862
- stmt,
36863
- pushdownMeta.fieldTypesByApp.get(stmt.from.appId),
36864
- pushdownMeta.fieldOptionsByApp.get(stmt.from.appId)
36865
- );
36866
- const tableConditions = /* @__PURE__ */ new Map();
36867
- if (stmt.where !== null) {
36868
- for (const join of stmt.joins) {
36869
- if (!join.table.alias || join.table.subtableCode || join.table.cteName !== null) continue;
36870
- const cond = extractSafePushdownLeaves(stmt.where, {
36871
- tableAlias: join.table.alias,
36872
- fieldTypes: pushdownMeta.fieldTypesByApp.get(join.table.appId),
36873
- fieldOptions: pushdownMeta.fieldOptionsByApp.get(join.table.appId)
36874
- });
36875
- if (cond) tableConditions.set(join.table.alias, cond);
36876
- }
36877
- }
37030
+ const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
37031
+ validateKlikePushdownPlan(pushdownPlan);
37032
+ const mainPushDown = pushdownPlan.mainCondition;
37033
+ const tableConditions = pushdownPlan.joinConditions;
36878
37034
  const mainFetch = fetchTableRecordsForFullScan(
36879
37035
  stmt,
36880
37036
  stmt.from,
@@ -36955,7 +37111,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
36955
37111
  optionOrders,
36956
37112
  sortKinds,
36957
37113
  fieldTypeResolver: fieldTypeResolvers.row,
36958
- havingFieldTypeResolver: fieldTypeResolvers.having
37114
+ havingFieldTypeResolver: fieldTypeResolvers.having,
37115
+ appliedKlikes: pushdownPlan.appliedKlikes
36959
37116
  });
36960
37117
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
36961
37118
  }
@@ -37004,83 +37161,6 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
37004
37161
  }
37005
37162
  return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
37006
37163
  }
37007
- function canInlineSingleCte(stmt) {
37008
- if (stmt.ctes.length !== 1) return false;
37009
- const cteDef = stmt.ctes[0];
37010
- if (cteDef.query.type !== "SELECT") return false;
37011
- if (resolveSelectMode(cteDef.query) !== "SIMPLE") return false;
37012
- const finalQuery = stmt.query;
37013
- if (finalQuery.type !== "SELECT") return false;
37014
- if (finalQuery.from.cteName !== cteDef.name) return false;
37015
- if (finalQuery.joins.length > 0) return false;
37016
- if (finalQuery.groupBy.length > 0) return false;
37017
- if (finalQuery.distinct) return false;
37018
- if (finalQuery.columns.some(
37019
- (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"
37020
- )) return false;
37021
- return true;
37022
- }
37023
- function buildInlinedQuery(stmt) {
37024
- const cteBody = stmt.ctes[0].query;
37025
- const final = stmt.query;
37026
- const cteAlias = final.from.alias;
37027
- const finalWhere = stripCteAlias(final.where, cteAlias);
37028
- let mergedWhere;
37029
- if (cteBody.where === null) mergedWhere = finalWhere;
37030
- else if (finalWhere === null) mergedWhere = cteBody.where;
37031
- else mergedWhere = { type: "LOGICAL", op: "AND", left: cteBody.where, right: finalWhere };
37032
- const columns = final.columns.every((c) => c.type === "WILDCARD") ? cteBody.columns : final.columns;
37033
- return {
37034
- type: "SELECT",
37035
- from: cteBody.from,
37036
- joins: [],
37037
- columns,
37038
- where: mergedWhere,
37039
- groupBy: [],
37040
- having: null,
37041
- orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
37042
- limit: final.limit ?? cteBody.limit,
37043
- offset: final.offset ?? cteBody.offset,
37044
- distinct: false
37045
- };
37046
- }
37047
- function stripCteAlias(where, alias) {
37048
- if (where === null || alias === null) return where;
37049
- switch (where.type) {
37050
- case "BINARY":
37051
- return {
37052
- type: "BINARY",
37053
- op: where.op,
37054
- left: stripCteAliasFromFieldValue(where.left, alias),
37055
- right: where.right
37056
- };
37057
- case "NULL_CHECK":
37058
- return {
37059
- type: "NULL_CHECK",
37060
- not: where.not,
37061
- field: stripCteAliasFromFieldValue(where.field, alias)
37062
- };
37063
- case "LOGICAL":
37064
- return {
37065
- type: "LOGICAL",
37066
- op: where.op,
37067
- left: stripCteAlias(where.left, alias),
37068
- right: stripCteAlias(where.right, alias)
37069
- };
37070
- case "NOT":
37071
- return { type: "NOT", expr: stripCteAlias(where.expr, alias) };
37072
- case "GROUP":
37073
- return { type: "GROUP", expr: stripCteAlias(where.expr, alias) };
37074
- case "EXISTS":
37075
- return where;
37076
- }
37077
- }
37078
- function stripCteAliasFromFieldValue(fv, alias) {
37079
- if (fv.type === "FIELD" && fv.tableAlias === alias) {
37080
- return { type: "FIELD", field: fv.field, tableAlias: null };
37081
- }
37082
- return fv;
37083
- }
37084
37164
  async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
37085
37165
  if (query.type === "UNION") {
37086
37166
  const [leftResult, rightResult] = await Promise.all([
@@ -37100,7 +37180,7 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
37100
37180
  const rows = query.all ? combined : deduplicateRows(combined, leftCols);
37101
37181
  return { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
37102
37182
  }
37103
- const hasCteRef = query.from.cteName != null || query.joins.some((j) => j.table.cteName != null);
37183
+ const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
37104
37184
  if (!hasCteRef) {
37105
37185
  return executeSelect(query, client, options, cacheContext, cteCache);
37106
37186
  }
@@ -37115,8 +37195,13 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37115
37195
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
37116
37196
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
37117
37197
  ]);
37118
- const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
37198
+ const [pushdownMeta, typedInFieldTypes] = await Promise.all([
37199
+ loadTypedPushdownMeta(stmt, client, cacheContext),
37200
+ loadTypedInFieldTypes(stmt, client, cacheContext)
37201
+ ]);
37119
37202
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
37203
+ const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
37204
+ validateKlikePushdownPlan(pushdownPlan);
37120
37205
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
37121
37206
  const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
37122
37207
  scalarCachePromise.catch(() => {
@@ -37136,7 +37221,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37136
37221
  parallel,
37137
37222
  true,
37138
37223
  options.onLimitReached ?? "error",
37139
- warnings
37224
+ warnings,
37225
+ pushdownPlan.mainCondition
37140
37226
  );
37141
37227
  tables.set(stmt.from.alias, mainRecords);
37142
37228
  }
@@ -37145,6 +37231,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37145
37231
  const rows2 = cteCache.get(join.table.cteName) ?? [];
37146
37232
  tables.set(join.table.alias, rows2.map(processRowToKintoneRecord));
37147
37233
  } else {
37234
+ const pushDownCond = join.table.alias ? pushdownPlan.joinConditions.get(join.table.alias) ?? null : null;
37148
37235
  const optimized = await tryFetchJoinRecordsBySourceKeys(
37149
37236
  stmt,
37150
37237
  join,
@@ -37153,7 +37240,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37153
37240
  maxRecords2,
37154
37241
  parallel,
37155
37242
  options.onLimitReached ?? "error",
37156
- warnings
37243
+ warnings,
37244
+ pushDownCond
37157
37245
  );
37158
37246
  const joinRecords = optimized ?? await fetchTableRecordsForFullScan(
37159
37247
  stmt,
@@ -37163,7 +37251,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37163
37251
  parallel,
37164
37252
  false,
37165
37253
  options.onLimitReached ?? "error",
37166
- warnings
37254
+ warnings,
37255
+ pushDownCond
37167
37256
  );
37168
37257
  tables.set(join.table.alias, joinRecords);
37169
37258
  }
@@ -37178,7 +37267,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37178
37267
  optionOrders,
37179
37268
  sortKinds,
37180
37269
  fieldTypeResolver: fieldTypeResolvers.row,
37181
- havingFieldTypeResolver: fieldTypeResolvers.having
37270
+ havingFieldTypeResolver: fieldTypeResolvers.having,
37271
+ appliedKlikes: pushdownPlan.appliedKlikes
37182
37272
  });
37183
37273
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
37184
37274
  }
@@ -38387,9 +38477,10 @@ function buildSelectPlan(stmt, label) {
38387
38477
  lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
38388
38478
  lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
38389
38479
  } else {
38480
+ const pushdownPlan = buildKlikePushdownPlan(stmt);
38390
38481
  const mainFields = selectToFetchAllFields(stmt, stmt.from);
38391
38482
  const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
38392
- const mainPushDown = extractMainSafePushdown(stmt);
38483
+ const mainPushDown = pushdownPlan.mainCondition;
38393
38484
  const mainCandidate = extractMainTypedPushdownCandidate(stmt);
38394
38485
  const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
38395
38486
  lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
@@ -38402,7 +38493,7 @@ function buildSelectPlan(stmt, label) {
38402
38493
  const joinFields = selectToFetchAllFields(stmt, join.table);
38403
38494
  const joinAliasStr = join.table.alias ? ` AS ${join.table.alias}` : "";
38404
38495
  const joinType = join.type === "INNER" ? "JOIN" : `${join.type} JOIN`;
38405
- const joinPushDown = join.table.alias && !join.table.subtableCode && join.table.cteName === null && stmt.where ? extractSafePushdownLeaves(stmt.where, { tableAlias: join.table.alias }) : null;
38496
+ const joinPushDown = join.table.alias ? pushdownPlan.joinConditions.get(join.table.alias) ?? null : null;
38406
38497
  const joinCandidate = join.table.alias && !join.table.subtableCode && join.table.cteName === null && stmt.where ? extractTypedPushdownCandidates(stmt.where, { tableAlias: join.table.alias }) : null;
38407
38498
  const joinQ = joinPushDown !== null ? whereToKintone(joinPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
38408
38499
  lines.push(` ${joinType}: APP${join.table.appId}${joinAliasStr} (${join.table.appId})`);
@@ -38445,6 +38536,10 @@ function buildWithPlan(stmt) {
38445
38536
  if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
38446
38537
  lines.push(...buildExplainPlan(stmt.query, "[main]"));
38447
38538
  }
38539
+ if (canInlineSingleCte(stmt)) {
38540
+ lines.push("");
38541
+ lines.push(...buildSelectPlan(buildInlinedQuery(stmt), "[effective: inlined CTE]"));
38542
+ }
38448
38543
  return lines;
38449
38544
  }
38450
38545
  function collectFullScanReasons(stmt) {
@@ -39122,10 +39217,11 @@ function detectSortKind(fieldType, calcFormat) {
39122
39217
  }
39123
39218
 
39124
39219
  // src/cli/nodeKintoneClient.ts
39220
+ var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
39125
39221
  function createNodeKintoneClient(baseUrl, tokenResolver) {
39126
39222
  const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
39127
39223
  const apiBasePath = tokenResolver.guestSpaceId && tokenResolver.guestSpaceId > 0 ? `/k/guest/${tokenResolver.guestSpaceId}/v1` : "/k/v1";
39128
- async function requestJson(path, init, appIdForToken) {
39224
+ async function requestJsonResponse(path, init, appIdForToken) {
39129
39225
  const headers = new Headers(init.headers ?? {});
39130
39226
  if (tokenResolver.auth.type === "token") {
39131
39227
  headers.set("X-Cybozu-API-Token", tokenResolver.auth.resolveToken(appIdForToken));
@@ -39173,7 +39269,14 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
39173
39269
  if (tokenResolver.debug) {
39174
39270
  tokenResolver.log?.(`[debug] response status=${res.status}`);
39175
39271
  }
39176
- return await res.json();
39272
+ const warning = res.headers.get("X-Cybozu-Warning") ?? "";
39273
+ return {
39274
+ body: await res.json(),
39275
+ searchAborted: warning.includes(SEARCH_ABORTED_HEADER_VALUE)
39276
+ };
39277
+ }
39278
+ async function requestJson(path, init, appIdForToken) {
39279
+ return (await requestJsonResponse(path, init, appIdForToken)).body;
39177
39280
  }
39178
39281
  function shouldRetryWithRecordNumberOrder(path, bodyText) {
39179
39282
  if (!path.includes("/v1/records.json?")) return false;
@@ -39207,11 +39310,12 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
39207
39310
  }
39208
39311
  const path = `${apiBasePath}/records.json?${qs}`;
39209
39312
  try {
39210
- return await requestJson(
39313
+ const response = await requestJsonResponse(
39211
39314
  path,
39212
39315
  { method: "GET" },
39213
39316
  params.app
39214
39317
  );
39318
+ return response.searchAborted ? { ...response.body, searchAborted: true } : response.body;
39215
39319
  } catch (err) {
39216
39320
  const msg = err instanceof Error ? err.message : String(err);
39217
39321
  if (!shouldRetryWithRecordNumberOrder(path, msg)) throw err;
@@ -39219,11 +39323,12 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
39219
39323
  if (tokenResolver.debug) {
39220
39324
  tokenResolver.log?.("[debug] retry with fallback query order by \u30EC\u30B3\u30FC\u30C9\u756A\u53F7 asc");
39221
39325
  }
39222
- return await requestJson(
39326
+ const response = await requestJsonResponse(
39223
39327
  retryPath,
39224
39328
  { method: "GET" },
39225
39329
  params.app
39226
39330
  );
39331
+ return response.searchAborted ? { ...response.body, searchAborted: true } : response.body;
39227
39332
  }
39228
39333
  },
39229
39334
  async postRecords(_params) {
@@ -40781,7 +40886,7 @@ Options:
40781
40886
  -h, --help Show help
40782
40887
  `);
40783
40888
  }
40784
- var SERVER_VERSION = true ? "2.8.0" : "0.0.0-dev";
40889
+ var SERVER_VERSION = true ? "2.10.0" : "0.0.0-dev";
40785
40890
  function createServer(args) {
40786
40891
  const server = new McpServer({
40787
40892
  name: "ksql-mcp",