@rex0220/kintone-sql-tools 2.17.0 → 3.1.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.
@@ -30990,6 +30990,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
30990
30990
  ["BY", "BY" /* BY */],
30991
30991
  ["HAVING", "HAVING" /* HAVING */],
30992
30992
  ["ORDER", "ORDER" /* ORDER */],
30993
+ ["KORDER", "KORDER" /* KORDER */],
30993
30994
  ["ASC", "ASC" /* ASC */],
30994
30995
  ["DESC", "DESC" /* DESC */],
30995
30996
  ["LIMIT", "LIMIT" /* LIMIT */],
@@ -31515,7 +31516,7 @@ var Parser = class {
31515
31516
  case "WITH" /* WITH */:
31516
31517
  return this.parseWith();
31517
31518
  case "SELECT" /* SELECT */:
31518
- return this.tryParseUnionChain(this.parseSelect());
31519
+ return this.tryParseUnionChain(this.parseSelect(true));
31519
31520
  case "INSERT" /* INSERT */:
31520
31521
  return this.parseInsert();
31521
31522
  case "UPDATE" /* UPDATE */:
@@ -31705,7 +31706,7 @@ var Parser = class {
31705
31706
  }
31706
31707
  query = w;
31707
31708
  } else if (tok.kind === "SELECT" /* SELECT */) {
31708
- const sel = this.parseSelect();
31709
+ const sel = this.parseSelect(true);
31709
31710
  const chained = this.tryParseUnionChain(sel);
31710
31711
  query = chained;
31711
31712
  } else if (tok.kind === "INSERT" /* INSERT */) {
@@ -31898,7 +31899,7 @@ var Parser = class {
31898
31899
  // ----------------------------------------------------------
31899
31900
  // SELECT
31900
31901
  // ----------------------------------------------------------
31901
- parseSelect() {
31902
+ parseSelect(allowKorder = false) {
31902
31903
  this.expect("SELECT" /* SELECT */);
31903
31904
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
31904
31905
  const columns = this.parseSelectColumns();
@@ -31915,7 +31916,19 @@ var Parser = class {
31915
31916
  having = this.parseWhereExpr();
31916
31917
  }
31917
31918
  }
31918
- const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
31919
+ let orderMode = "CANONICAL";
31920
+ let orderBy = [];
31921
+ if (this.consume("ORDER" /* ORDER */)) {
31922
+ this.expect("BY" /* BY */);
31923
+ orderBy = this.parseOrderBy();
31924
+ } else if (this.consume("KORDER" /* KORDER */)) {
31925
+ if (!allowKorder) {
31926
+ throw new ParseError("KORDER BY \u306F\u5229\u7528\u8005\u3078\u7D50\u679C\u3092\u8FD4\u3059\u30C8\u30C3\u30D7\u30EC\u30D9\u30EB SELECT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", this.prev());
31927
+ }
31928
+ orderMode = "KINTONE_NATIVE";
31929
+ this.expect("BY" /* BY */);
31930
+ orderBy = this.parseOrderBy();
31931
+ }
31919
31932
  const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
31920
31933
  const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
31921
31934
  const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
@@ -31932,6 +31945,7 @@ var Parser = class {
31932
31945
  where,
31933
31946
  groupBy,
31934
31947
  having,
31948
+ orderMode,
31935
31949
  orderBy,
31936
31950
  limit,
31937
31951
  offset
@@ -31969,6 +31983,9 @@ var Parser = class {
31969
31983
  // ----------------------------------------------------------
31970
31984
  tryParseUnionChain(left) {
31971
31985
  if (this.peek().kind !== "UNION" /* UNION */) return left;
31986
+ if (left.type === "SELECT" && left.orderMode === "KINTONE_NATIVE") {
31987
+ throw new ParseError("KORDER BY \u306F UNION \u5206\u5C90\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
31988
+ }
31972
31989
  this.advance();
31973
31990
  const all = this.consume("ALL" /* ALL */);
31974
31991
  const right = this.parseSelect();
@@ -33622,7 +33639,50 @@ function isReadOnlyStatement(stmt) {
33622
33639
  return !writesKintone(stmt) && (isReadOnlyType(stmt.type) || isDmlType(stmt.type));
33623
33640
  }
33624
33641
  function requiresCompleteInput(stmt) {
33625
- return isDmlType(stmt.type);
33642
+ if (isDmlType(stmt.type)) return true;
33643
+ switch (stmt.type) {
33644
+ case "SELECT":
33645
+ return selectRequiresCompleteInput(stmt);
33646
+ case "UNION":
33647
+ return unionRequiresCompleteInput(stmt);
33648
+ case "WITH":
33649
+ return stmt.ctes.some(
33650
+ (cte) => cte.query.type === "SELECT" && selectRequiresCompleteInput(cte.query) || cte.query.type === "UNION" && unionRequiresCompleteInput(cte.query)
33651
+ ) || (stmt.query.type === "SELECT" ? selectRequiresCompleteInput(stmt.query) : unionRequiresCompleteInput(stmt.query));
33652
+ case "CREATE_TEMP_TABLE":
33653
+ return stmt.query.type === "SELECT" ? selectRequiresCompleteInput(stmt.query) : stmt.query.type === "UNION" ? unionRequiresCompleteInput(stmt.query) : requiresCompleteInput(stmt.query);
33654
+ default:
33655
+ return false;
33656
+ }
33657
+ }
33658
+ function unionRequiresCompleteInput(stmt) {
33659
+ const left = stmt.left.type === "SELECT" ? selectRequiresCompleteInput(stmt.left) : unionRequiresCompleteInput(stmt.left);
33660
+ return left || selectRequiresCompleteInput(stmt.right);
33661
+ }
33662
+ function selectRequiresCompleteInput(stmt) {
33663
+ if (stmt.orderBy.length > 0) return true;
33664
+ if (stmt.columns.some(
33665
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0 || column.type === "SCALAR_SUBQUERY_COL" && selectRequiresCompleteInput(column.query) || column.type === "CASE_COL" && column.expr.branches.some(
33666
+ (branch) => whereRequiresCompleteInput(branch.condition)
33667
+ )
33668
+ )) return true;
33669
+ return whereRequiresCompleteInput(stmt.where) || whereRequiresCompleteInput(stmt.having);
33670
+ }
33671
+ function whereRequiresCompleteInput(where) {
33672
+ if (where === null) return false;
33673
+ switch (where.type) {
33674
+ case "BINARY":
33675
+ return (where.right.type === "SUBQUERY_IN_LIST" || where.right.type === "SCALAR_SUBQUERY") && selectRequiresCompleteInput(where.right.query);
33676
+ case "LOGICAL":
33677
+ return whereRequiresCompleteInput(where.left) || whereRequiresCompleteInput(where.right);
33678
+ case "NOT":
33679
+ case "GROUP":
33680
+ return whereRequiresCompleteInput(where.expr);
33681
+ case "EXISTS":
33682
+ return selectRequiresCompleteInput(where.query);
33683
+ case "NULL_CHECK":
33684
+ return false;
33685
+ }
33626
33686
  }
33627
33687
  function hasWhereClause(stmt) {
33628
33688
  if (!stmt || typeof stmt !== "object") return false;
@@ -34425,6 +34485,7 @@ function buildInlinedQuery(stmt) {
34425
34485
  where,
34426
34486
  groupBy: [],
34427
34487
  having: null,
34488
+ orderMode: "CANONICAL",
34428
34489
  orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
34429
34490
  limit: final.limit ?? cteBody.limit,
34430
34491
  offset: final.offset ?? cteBody.offset,
@@ -35019,6 +35080,72 @@ function analyzeBatch(statements) {
35019
35080
  };
35020
35081
  }
35021
35082
 
35083
+ // src/core/fieldSemantics.ts
35084
+ var STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
35085
+ "SINGLE_LINE_TEXT",
35086
+ "MULTI_LINE_TEXT",
35087
+ "RICH_TEXT",
35088
+ "LINK",
35089
+ "DATE",
35090
+ "TIME",
35091
+ "DATETIME",
35092
+ "CREATED_TIME",
35093
+ "UPDATED_TIME",
35094
+ "CREATOR",
35095
+ "MODIFIER"
35096
+ ]);
35097
+ var OPTION_FIELD_TYPES = /* @__PURE__ */ new Set([
35098
+ "DROP_DOWN",
35099
+ "RADIO_BUTTON",
35100
+ "CHECK_BOX",
35101
+ "MULTI_SELECT",
35102
+ "STATUS"
35103
+ ]);
35104
+ function resolveFieldSemantics(source) {
35105
+ let compareMode;
35106
+ if (source.fieldType === "RECORD_NUMBER" || source.fieldType === "__ID__") {
35107
+ compareMode = "recordNumber";
35108
+ } else if (source.fieldType === "NUMBER") {
35109
+ compareMode = "number";
35110
+ } else if (source.fieldType === "CALC") {
35111
+ compareMode = source.sortKind === "number" ? "number" : "string";
35112
+ } else if (OPTION_FIELD_TYPES.has(source.fieldType)) {
35113
+ compareMode = "option";
35114
+ } else if (STRING_FIELD_TYPES.has(source.fieldType)) {
35115
+ compareMode = "string";
35116
+ } else {
35117
+ compareMode = "unsupported";
35118
+ }
35119
+ const optionOrder = source.optionOrder ? new Map(Object.entries(source.optionOrder)) : void 0;
35120
+ return {
35121
+ fieldType: source.fieldType,
35122
+ compareMode,
35123
+ inSubtable: source.inSubtable === true,
35124
+ requiresCollectionOperators: source.inSubtable === true || source.requiresCollectionOperators === true,
35125
+ ...optionOrder && optionOrder.size > 0 ? { optionOrder } : {}
35126
+ };
35127
+ }
35128
+ function syntheticSemantics(compareMode, fieldType = compareMode === "number" ? "KSQL_NUMBER" : "KSQL_STRING") {
35129
+ return { fieldType, compareMode, inSubtable: false, requiresCollectionOperators: false };
35130
+ }
35131
+ function withFieldSemanticSource(semantics, appId, fieldCode) {
35132
+ return { ...semantics, source: { appId, fieldCode } };
35133
+ }
35134
+ function fieldSemanticsEqual(left, right) {
35135
+ if (left === right) return true;
35136
+ if (!left || !right) return false;
35137
+ if (left.fieldType !== right.fieldType || left.compareMode !== right.compareMode || left.inSubtable !== right.inSubtable || left.requiresCollectionOperators !== right.requiresCollectionOperators) return false;
35138
+ if (left.source?.appId !== right.source?.appId || left.source?.fieldCode !== right.source?.fieldCode) return false;
35139
+ const a = left.optionOrder;
35140
+ const b = right.optionOrder;
35141
+ if (a === b) return true;
35142
+ if (!a || !b || a.size !== b.size) return false;
35143
+ for (const [key, value] of a) {
35144
+ if (b.get(key) !== value) return false;
35145
+ }
35146
+ return true;
35147
+ }
35148
+
35022
35149
  // src/core/batchVariables.ts
35023
35150
  var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
35024
35151
  function normalizeBatchVariableName(name) {
@@ -35054,24 +35181,129 @@ function validateDeclaredBatchVariables(statements, input) {
35054
35181
  }
35055
35182
 
35056
35183
  // src/core/scalarCompare.ts
35057
- function compareScalarValues(op, leftStr, rightStr) {
35058
- if (op === "=") return leftStr === rightStr;
35059
- if (op === "!=" || op === "<>") return leftStr !== rightStr;
35060
- const rightNum = Number(rightStr);
35061
- if (leftStr === "" && rightStr !== "" && Number.isFinite(rightNum)) {
35062
- return op === "<" || op === "<=";
35063
- }
35064
- const leftNum = Number(leftStr);
35065
- const numeric = !Number.isNaN(leftNum) && !Number.isNaN(rightNum);
35184
+ function compareCodePointStrings(left, right) {
35185
+ const a = left[Symbol.iterator]();
35186
+ const b = right[Symbol.iterator]();
35187
+ while (true) {
35188
+ const av = a.next();
35189
+ const bv = b.next();
35190
+ if (av.done || bv.done) {
35191
+ if (av.done && bv.done) return 0;
35192
+ return av.done ? -1 : 1;
35193
+ }
35194
+ const ac = av.value.codePointAt(0) ?? 0;
35195
+ const bc = bv.value.codePointAt(0) ?? 0;
35196
+ if (ac < bc) return -1;
35197
+ if (ac > bc) return 1;
35198
+ }
35199
+ }
35200
+ function triCompare(left, right) {
35201
+ return left < right ? -1 : left > right ? 1 : 0;
35202
+ }
35203
+ function numberKey(value) {
35204
+ if (value === "") return { band: 0 };
35205
+ const numeric = Number(value);
35206
+ if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
35207
+ if (Number.isFinite(numeric)) return { band: 2, value: numeric };
35208
+ if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
35209
+ if (value === "NaN") return { band: 4 };
35210
+ return { band: 5, value };
35211
+ }
35212
+ function compareNumbers(left, right) {
35213
+ const a = numberKey(left);
35214
+ const b = numberKey(right);
35215
+ if (a.band !== b.band) return a.band < b.band ? -1 : 1;
35216
+ if (a.band === 2 && b.band === 2) return triCompare(a.value, b.value);
35217
+ if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
35218
+ return 0;
35219
+ }
35220
+ function recordNumberKey(value, allowPrefix) {
35221
+ if (value === "") return { empty: true, normalizedId: "", display: value };
35222
+ const match = /^\d+$/.test(value) ? value : allowPrefix ? /-(\d+)$/.exec(value)?.[1] : void 0;
35223
+ if (match === void 0) {
35224
+ throw new Error(`ArgumentError: invalid ${allowPrefix ? "RECORD_NUMBER" : "$id"} value: ${value}`);
35225
+ }
35226
+ return {
35227
+ empty: false,
35228
+ normalizedId: match.replace(/^0+(?=\d)/, ""),
35229
+ display: value
35230
+ };
35231
+ }
35232
+ function compareRecordNumbers(left, right, allowPrefix) {
35233
+ const a = recordNumberKey(left, allowPrefix);
35234
+ const b = recordNumberKey(right, allowPrefix);
35235
+ if (a.empty || b.empty) return a.empty === b.empty ? 0 : a.empty ? -1 : 1;
35236
+ if (a.normalizedId.length !== b.normalizedId.length) {
35237
+ return a.normalizedId.length < b.normalizedId.length ? -1 : 1;
35238
+ }
35239
+ const idCmp = compareCodePointStrings(a.normalizedId, b.normalizedId);
35240
+ return idCmp !== 0 ? idCmp : compareCodePointStrings(a.display, b.display);
35241
+ }
35242
+ function parseOptionValues(value, fieldType) {
35243
+ if (value === "") return [];
35244
+ if (fieldType !== "CHECK_BOX" && fieldType !== "MULTI_SELECT") return [value];
35245
+ try {
35246
+ const parsed = JSON.parse(value);
35247
+ return Array.isArray(parsed) ? parsed.map((item) => String(item ?? "")) : [value];
35248
+ } catch {
35249
+ return [value];
35250
+ }
35251
+ }
35252
+ function optionVector(value, semantics) {
35253
+ const order = semantics.optionOrder ?? /* @__PURE__ */ new Map();
35254
+ const unique = [...new Set(parseOptionValues(value, semantics.fieldType))];
35255
+ const vector = unique.map((label) => {
35256
+ const rank = order.get(label);
35257
+ return rank === void 0 ? { knownBand: 1, rank: 0, label } : { knownBand: 0, rank, label };
35258
+ });
35259
+ vector.sort(compareOptionElement);
35260
+ return vector;
35261
+ }
35262
+ function compareOptionElement(left, right) {
35263
+ if (left.knownBand !== right.knownBand) return left.knownBand < right.knownBand ? -1 : 1;
35264
+ if (left.rank !== right.rank) return left.rank < right.rank ? -1 : 1;
35265
+ return compareCodePointStrings(left.label, right.label);
35266
+ }
35267
+ function compareOptions(left, right, semantics) {
35268
+ const a = optionVector(left, semantics);
35269
+ const b = optionVector(right, semantics);
35270
+ const length = Math.min(a.length, b.length);
35271
+ for (let index = 0; index < length; index++) {
35272
+ const cmp = compareOptionElement(a[index], b[index]);
35273
+ if (cmp !== 0) return cmp;
35274
+ }
35275
+ return a.length < b.length ? -1 : a.length > b.length ? 1 : 0;
35276
+ }
35277
+ function compareCanonicalValues(left, right, semantics) {
35278
+ switch (semantics.compareMode) {
35279
+ case "string":
35280
+ return compareCodePointStrings(left, right);
35281
+ case "number":
35282
+ return compareNumbers(left, right);
35283
+ case "recordNumber":
35284
+ return compareRecordNumbers(left, right, semantics.fieldType === "RECORD_NUMBER");
35285
+ case "option":
35286
+ return compareOptions(left, right, semantics);
35287
+ case "unsupported":
35288
+ throw new Error(`ArgumentError: values of type ${semantics.fieldType} cannot be compared.`);
35289
+ }
35290
+ }
35291
+ function compareScalarValues(op, left, right, semantics = syntheticSemantics("string")) {
35292
+ const cmp = compareCanonicalValues(left, right, semantics);
35066
35293
  switch (op) {
35294
+ case "=":
35295
+ return cmp === 0;
35296
+ case "!=":
35297
+ case "<>":
35298
+ return cmp !== 0;
35067
35299
  case ">":
35068
- return numeric ? leftNum > rightNum : leftStr > rightStr;
35300
+ return cmp > 0;
35069
35301
  case "<":
35070
- return numeric ? leftNum < rightNum : leftStr < rightStr;
35302
+ return cmp < 0;
35071
35303
  case ">=":
35072
- return numeric ? leftNum >= rightNum : leftStr >= rightStr;
35304
+ return cmp >= 0;
35073
35305
  case "<=":
35074
- return numeric ? leftNum <= rightNum : leftStr <= rightStr;
35306
+ return cmp <= 0;
35075
35307
  }
35076
35308
  }
35077
35309
  function selectScalarExtreme(values, extreme) {
@@ -35081,12 +35313,10 @@ function selectScalarExtreme(values, extreme) {
35081
35313
  const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
35082
35314
  const compare = (left, right) => {
35083
35315
  if (numeric) {
35084
- const leftNum = Number(left);
35085
- const rightNum = Number(right);
35086
- if (leftNum < rightNum) return -1;
35087
- if (leftNum > rightNum) return 1;
35316
+ const numericCmp = triCompare(Number(left), Number(right));
35317
+ if (numericCmp !== 0) return numericCmp;
35088
35318
  }
35089
- return left < right ? -1 : left > right ? 1 : 0;
35319
+ return compareCodePointStrings(left, right);
35090
35320
  };
35091
35321
  return candidates.reduce((best, candidate) => {
35092
35322
  const cmp = compare(candidate, best);
@@ -35094,6 +35324,55 @@ function selectScalarExtreme(values, extreme) {
35094
35324
  });
35095
35325
  }
35096
35326
 
35327
+ // src/core/explainMetadata.ts
35328
+ function whereNeedsFieldMetadata(where) {
35329
+ if (where === null) return false;
35330
+ switch (where.type) {
35331
+ case "BINARY":
35332
+ return valueNeedsFieldMetadata(where.left);
35333
+ case "NULL_CHECK":
35334
+ return valueNeedsFieldMetadata(where.field);
35335
+ case "LOGICAL":
35336
+ return whereNeedsFieldMetadata(where.left) || whereNeedsFieldMetadata(where.right);
35337
+ case "NOT":
35338
+ case "GROUP":
35339
+ return whereNeedsFieldMetadata(where.expr);
35340
+ case "EXISTS":
35341
+ return false;
35342
+ }
35343
+ }
35344
+ function valueNeedsFieldMetadata(value) {
35345
+ if (Array.isArray(value)) return value.some(valueNeedsFieldMetadata);
35346
+ if (value === null || typeof value !== "object") return false;
35347
+ const item = value;
35348
+ if (item["type"] === "FIELD") return item["field"] !== "$id";
35349
+ if (item["type"] === "SELECT") return false;
35350
+ return Object.values(item).some(valueNeedsFieldMetadata);
35351
+ }
35352
+ function selectNeedsOwnMetadata(statement) {
35353
+ return whereNeedsFieldMetadata(statement.where) || statement.orderBy.length > 0 || statement.columns.some(
35354
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
35355
+ );
35356
+ }
35357
+ function explainNeedsAppMetadata(statement) {
35358
+ const seen = /* @__PURE__ */ new Set();
35359
+ const visit = (node) => {
35360
+ if (node === null || typeof node !== "object") return false;
35361
+ if (seen.has(node)) return false;
35362
+ seen.add(node);
35363
+ if (Array.isArray(node)) return node.some(visit);
35364
+ const item = node;
35365
+ if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
35366
+ return true;
35367
+ }
35368
+ if ((item["type"] === "UPDATE" || item["type"] === "DELETE") && whereNeedsFieldMetadata(node.where)) {
35369
+ return true;
35370
+ }
35371
+ return Object.values(item).some(visit);
35372
+ };
35373
+ return visit(statement);
35374
+ }
35375
+
35097
35376
  // src/engine/evalFunc.ts
35098
35377
  function evalArithExpr(expr, row) {
35099
35378
  if (expr.type === "NUMBER") return expr.value;
@@ -35358,34 +35637,35 @@ function resolveFieldRef(row, field) {
35358
35637
  }
35359
35638
 
35360
35639
  // src/engine/evalWhere.ts
35361
- function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
35640
+ function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
35362
35641
  switch (expr.type) {
35363
35642
  case "BINARY":
35364
- return evalBinary(expr, row, resolveFieldType, appliedKlikes);
35643
+ return evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35365
35644
  case "NULL_CHECK":
35366
35645
  return evalNullCheck(expr, row);
35367
35646
  case "LOGICAL":
35368
- return evalLogical(expr, row, resolveFieldType, appliedKlikes);
35647
+ return evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35369
35648
  case "NOT":
35370
- return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
35649
+ return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35371
35650
  case "GROUP":
35372
- return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
35651
+ return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35373
35652
  case "EXISTS": {
35374
35653
  const exists = expr.resolved;
35375
35654
  return expr.not ? !exists : exists;
35376
35655
  }
35377
35656
  }
35378
35657
  }
35379
- function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
35658
+ function evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
35380
35659
  if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
35381
35660
  if (appliedKlikes?.has(expr)) return true;
35382
35661
  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");
35383
35662
  }
35384
- const left = resolveField(expr.left, row, resolveFieldType);
35663
+ const left = resolveField(expr.left, row, resolveFieldType, resolveFieldSemantics2);
35385
35664
  const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
35386
- return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
35665
+ const semantics = semanticsForLeft(expr.left, fieldType, resolveFieldSemantics2);
35666
+ return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType, semantics, resolveFieldSemantics2);
35387
35667
  }
35388
- function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
35668
+ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics = syntheticSemantics("string"), resolveFieldSemantics2) {
35389
35669
  if (op === "IN" || op === "NOT_IN") {
35390
35670
  let values = null;
35391
35671
  if (right.type === "IN_LIST") {
@@ -35410,8 +35690,53 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
35410
35690
  if (op === "KLIKE" || op === "NOT_KLIKE") {
35411
35691
  throw new Error("KLIKE / NOT KLIKE \u306F JavaScript \u5074\u3067\u306F\u8A55\u4FA1\u3067\u304D\u307E\u305B\u3093\uFF08SIMPLE SELECT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\uFF09");
35412
35692
  }
35413
- const rightStr = resolveValue(right, row, resolveFieldType);
35414
- return compareScalarValues(op, leftStr, rightStr);
35693
+ const rightStr = resolveValue(right, row, resolveFieldType, resolveFieldSemantics2);
35694
+ return compareScalarValues(op, leftStr, rightStr, semantics);
35695
+ }
35696
+ var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
35697
+ "LENGTH",
35698
+ "INSTR",
35699
+ "ROUND",
35700
+ "FLOOR",
35701
+ "CEIL",
35702
+ "TRUNCATE",
35703
+ "YEAR",
35704
+ "MONTH",
35705
+ "DAY",
35706
+ "DATEDIFF",
35707
+ "ABS",
35708
+ "MOD",
35709
+ "POWER",
35710
+ "SQRT"
35711
+ ]);
35712
+ function semanticsForLeft(left, fieldType, resolveSemantics) {
35713
+ if (left.type === "FIELD") {
35714
+ return resolveSemantics?.(left) ?? (fieldType ? resolveFieldSemantics({ fieldType }) : syntheticSemantics("string"));
35715
+ }
35716
+ if (left.type === "ARITH_FIELD") return syntheticSemantics("number");
35717
+ if (left.type === "FUNC_FIELD") {
35718
+ return syntheticSemantics(NUMERIC_STRING_FUNCTIONS.has(left.expr.func) ? "number" : "string");
35719
+ }
35720
+ if (left.type === "CASE_FIELD") {
35721
+ const results = [
35722
+ ...left.expr.branches.map((branch) => branch.result),
35723
+ ...left.expr.elseResult ? [left.expr.elseResult] : []
35724
+ ];
35725
+ const modes = results.map((result) => {
35726
+ if (result.type === "NUMBER" || result.type === "ARITH") return syntheticSemantics("number");
35727
+ if (result.type === "STRING_FUNC") {
35728
+ return syntheticSemantics(NUMERIC_STRING_FUNCTIONS.has(result.func) ? "number" : "string");
35729
+ }
35730
+ if (result.type === "FIELD_REF") {
35731
+ const dot = result.field.indexOf(".");
35732
+ const ref = dot > 0 ? { type: "FIELD", tableAlias: result.field.slice(0, dot), field: result.field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: result.field };
35733
+ return resolveSemantics?.(ref) ?? syntheticSemantics("string");
35734
+ }
35735
+ return syntheticSemantics("string");
35736
+ });
35737
+ if (modes.length > 0 && modes.every((mode) => mode.compareMode === modes[0].compareMode)) return modes[0];
35738
+ }
35739
+ return syntheticSemantics("string");
35415
35740
  }
35416
35741
  var STRING_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
35417
35742
  var OBJECT_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([
@@ -35462,20 +35787,20 @@ function evalNullCheck(expr, row) {
35462
35787
  const val = resolveField(expr.field, row);
35463
35788
  return expr.not ? val !== "" : val === "";
35464
35789
  }
35465
- function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
35790
+ function evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
35466
35791
  if (expr.op === "AND") {
35467
- return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
35792
+ return evalWhere(expr.left, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35468
35793
  }
35469
- return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
35794
+ return evalWhere(expr.left, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35470
35795
  }
35471
- function resolveField(field, row, resolveFieldType) {
35796
+ function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
35472
35797
  if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
35473
35798
  if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
35474
- if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType);
35799
+ if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
35475
35800
  const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
35476
35801
  return resolveFieldRef(row, key);
35477
35802
  }
35478
- function resolveValue(value, row, resolveFieldType) {
35803
+ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
35479
35804
  switch (value.type) {
35480
35805
  case "VARIABLE":
35481
35806
  throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
@@ -35498,14 +35823,14 @@ function resolveValue(value, row, resolveFieldType) {
35498
35823
  if (value.expr.type === "STRING_FUNC") return evalStringFunc(value.expr, row);
35499
35824
  return String(evalArithExpr(value.expr, row));
35500
35825
  case "CASE_VALUE":
35501
- return evalCaseWhen(value.expr, row, resolveFieldType);
35826
+ return evalCaseWhen(value.expr, row, resolveFieldType, resolveFieldSemantics2);
35502
35827
  case "ARRAY":
35503
35828
  return value.elements.map((e) => e.value).join(",");
35504
35829
  }
35505
35830
  }
35506
- function evalCaseWhen(expr, row, resolveFieldType) {
35831
+ function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
35507
35832
  for (const branch of expr.branches) {
35508
- if (evalWhere(branch.condition, row, resolveFieldType)) {
35833
+ if (evalWhere(branch.condition, row, resolveFieldType, void 0, resolveFieldSemantics2)) {
35509
35834
  return evalCaseResult(branch.result, row);
35510
35835
  }
35511
35836
  }
@@ -36136,6 +36461,232 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
36136
36461
  };
36137
36462
  }
36138
36463
 
36464
+ // src/core/optimization/canonicalOrderPlanner.ts
36465
+ var REST_OFFSET_MAX = 1e4;
36466
+ var REST_LIMIT_MAX = 500;
36467
+ function fieldSemantics(item, semantics) {
36468
+ return item.key.type === "FIELD_NAME" ? semantics.get(item.key.name) : void 0;
36469
+ }
36470
+ function planCanonicalOrder(input) {
36471
+ const { stmt } = input;
36472
+ const reasons = [];
36473
+ const windowOrderBy = stmt.columns.flatMap(
36474
+ (column) => column.type === "WINDOW_COL" ? column.orderBy : []
36475
+ );
36476
+ const allOrderBy = [...stmt.orderBy, ...windowOrderBy];
36477
+ for (const item of allOrderBy) {
36478
+ if (item.key.type !== "FIELD_NAME") continue;
36479
+ const semantics = fieldSemantics(item, input.orderSemantics);
36480
+ if (!semantics) {
36481
+ reasons.push("ORDER_KEY_UNRESOLVED");
36482
+ continue;
36483
+ }
36484
+ if (semantics.fieldType === "KSQL_AMBIGUOUS") reasons.push("ORDER_KEY_AMBIGUOUS");
36485
+ else if (semantics.compareMode === "unsupported") reasons.push("ORDER_KEY_UNSUPPORTED");
36486
+ }
36487
+ if (reasons.includes("ORDER_KEY_AMBIGUOUS")) {
36488
+ throw new Error(
36489
+ "ArgumentError: ORDER BY key is an ambiguous column reference (reason=ORDER_KEY_AMBIGUOUS). Qualify the key with its table alias."
36490
+ );
36491
+ }
36492
+ if (reasons.includes("ORDER_KEY_UNSUPPORTED") || reasons.includes("ORDER_KEY_UNRESOLVED")) {
36493
+ const reason = reasons.includes("ORDER_KEY_UNSUPPORTED") ? "ORDER_KEY_UNSUPPORTED" : "ORDER_KEY_UNRESOLVED";
36494
+ throw new Error(`ArgumentError: ORDER BY key has no canonical comparison contract (reason=${reason}).`);
36495
+ }
36496
+ const allRestEquivalent = stmt.orderBy.length > 0 && windowOrderBy.length === 0 && stmt.orderBy.every(
36497
+ (item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
36498
+ );
36499
+ if (!allRestEquivalent) reasons.push("ORDER_KEY_NOT_REST_EQUIVALENT");
36500
+ if (input.whereCapability !== "EXACT_PUSHDOWN") reasons.push("WHERE_NOT_EXACT");
36501
+ if (input.staticMode !== "SIMPLE") reasons.push("QUERY_SHAPE_LOCAL");
36502
+ if (stmt.limit === null || stmt.limit < 0 || stmt.limit > REST_LIMIT_MAX) {
36503
+ reasons.push("LIMIT_NOT_REST_WINDOW");
36504
+ }
36505
+ if ((stmt.offset ?? 0) < 0 || (stmt.offset ?? 0) > REST_OFFSET_MAX) {
36506
+ reasons.push("OFFSET_NOT_REST_WINDOW");
36507
+ }
36508
+ if (stmt.limit !== null && stmt.limit > input.maxRecords) reasons.push("MAX_RECORDS_WINDOW");
36509
+ if (input.hasKlike) reasons.push("KLIKE_NOT_REST_WINDOW");
36510
+ if (reasons.length === 0) {
36511
+ return {
36512
+ kind: "CANONICAL_REST_TOP_N",
36513
+ requiresCompleteInput: false,
36514
+ localOrderBy: false,
36515
+ applyLocalOffsetLimit: false,
36516
+ reasonCodes: []
36517
+ };
36518
+ }
36519
+ return {
36520
+ kind: "CANONICAL_LOCAL",
36521
+ requiresCompleteInput: allOrderBy.length > 0,
36522
+ localOrderBy: stmt.orderBy.length > 0,
36523
+ applyLocalOffsetLimit: stmt.orderBy.length > 0,
36524
+ reasonCodes: [...new Set(reasons)]
36525
+ };
36526
+ }
36527
+
36528
+ // src/core/optimization/korderPlanner.ts
36529
+ var KORDER_NATIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
36530
+ "RECORD_NUMBER",
36531
+ "SINGLE_LINE_TEXT",
36532
+ "NUMBER",
36533
+ "CALC",
36534
+ "DATE",
36535
+ "DATETIME",
36536
+ "TIME",
36537
+ "CREATED_TIME",
36538
+ "UPDATED_TIME",
36539
+ "DROP_DOWN",
36540
+ "RADIO_BUTTON",
36541
+ "STATUS",
36542
+ "LINK",
36543
+ "CREATOR",
36544
+ "MODIFIER"
36545
+ ]);
36546
+ function planKorder(input) {
36547
+ const { stmt } = input;
36548
+ const reasons = [];
36549
+ if (stmt.orderMode !== "KINTONE_NATIVE") reasons.push("KORDER_MODE_REQUIRED");
36550
+ if (stmt.from.cteName !== null || stmt.from.subtableCode || input.staticMode !== "SIMPLE") {
36551
+ reasons.push("KORDER_QUERY_SHAPE_UNSUPPORTED");
36552
+ }
36553
+ if (input.whereCapability !== "EXACT_PUSHDOWN") reasons.push("KORDER_WHERE_NOT_EXACT");
36554
+ if (input.hasKlike) reasons.push("KORDER_KLIKE_UNSUPPORTED");
36555
+ if (stmt.orderBy.length === 0) reasons.push("KORDER_KEY_REQUIRED");
36556
+ for (const item of stmt.orderBy) {
36557
+ if (item.key.type !== "FIELD_NAME") {
36558
+ reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(key=${item.key.type})`);
36559
+ continue;
36560
+ }
36561
+ const name = item.key.name;
36562
+ const semantics = input.orderSemantics.get(name);
36563
+ if (!semantics) {
36564
+ reasons.push(`KORDER_KEY_UNRESOLVED(field=${name})`);
36565
+ continue;
36566
+ }
36567
+ if (name === "$id") continue;
36568
+ if (!semantics.source || semantics.source.fieldCode !== name) {
36569
+ reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(field=${name})`);
36570
+ continue;
36571
+ }
36572
+ if (!KORDER_NATIVE_FIELD_TYPES.has(semantics.fieldType)) {
36573
+ reasons.push(`KORDER_TYPE_UNSUPPORTED(field=${name}, type=${semantics.fieldType})`);
36574
+ }
36575
+ }
36576
+ if (stmt.limit === null || !Number.isSafeInteger(stmt.limit) || stmt.limit < 0) {
36577
+ reasons.push(`KORDER_LIMIT_INVALID(limit=${String(stmt.limit)})`);
36578
+ }
36579
+ const offset = stmt.offset ?? 0;
36580
+ if (!Number.isSafeInteger(offset) || offset < 0) {
36581
+ reasons.push(`KORDER_OFFSET_INVALID(offset=${offset})`);
36582
+ }
36583
+ const scanRows = stmt.limit === null ? Number.NaN : offset + stmt.limit;
36584
+ if (stmt.limit !== null && !Number.isSafeInteger(scanRows)) {
36585
+ reasons.push(`KORDER_SCAN_ROWS_INVALID(offset=${offset}, limit=${stmt.limit})`);
36586
+ }
36587
+ const unique = [...new Set(reasons)];
36588
+ if (unique.length > 0) {
36589
+ throw new Error(
36590
+ `ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
36591
+ );
36592
+ }
36593
+ const native = stmt.limit <= 500 && offset <= 1e4 && stmt.limit <= input.maxRecords;
36594
+ if (!native && scanRows > input.maxRecords) {
36595
+ throw new Error(
36596
+ `ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; KORDER_SCAN_ROWS_EXCEEDS_MAX_RECORDS(scanRows=${scanRows}, maxRecords=${input.maxRecords})). Use ORDER BY for canonical local ordering, raise maxRecords, or reduce LIMIT/OFFSET.`
36597
+ );
36598
+ }
36599
+ return {
36600
+ kind: native ? "KORDER_NATIVE" : "KORDER_CURSOR",
36601
+ requiresCompleteInput: false,
36602
+ localOrderBy: false,
36603
+ applyLocalOffsetLimit: false,
36604
+ reasonCodes: [],
36605
+ scanRows
36606
+ };
36607
+ }
36608
+
36609
+ // src/core/errors/cursorErrors.ts
36610
+ var CursorCapacityError = class extends Error {
36611
+ constructor(host, limit, waitMs) {
36612
+ super(`CursorCapacityError: host=${host} \u306E active cursor \u4E0A\u9650 ${limit} \u306B ${waitMs}ms \u4EE5\u5185\u3067\u7A7A\u304D\u304C\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002`);
36613
+ this.name = "CursorCapacityError";
36614
+ }
36615
+ };
36616
+ var CursorCreateOutcomeUnknownError = class extends Error {
36617
+ constructor(cause) {
36618
+ super("CursorCreateOutcomeUnknownError: Create Cursor \u306E\u6210\u5426\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u81EA\u52D5\u518D\u8A66\u884C\u305B\u305A\u3001\u6700\u592710\u5206+\u5B89\u5168\u4F59\u88D5\u306E\u9593\u306F\u67A0\u3092\u9694\u96E2\u3057\u307E\u3059\u3002");
36619
+ this.name = "CursorCreateOutcomeUnknownError";
36620
+ this.cause = cause;
36621
+ }
36622
+ };
36623
+ var CursorCleanupWarning = class extends Error {
36624
+ constructor(cause) {
36625
+ const detail = cause instanceof Error ? cause.message : String(cause);
36626
+ super(`CursorCleanupWarning: Cursor \u306E\u89E3\u653E\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u7D50\u679C\u306F\u6709\u52B9\u3067\u3059\u304C\u3001\u6700\u592710\u5206+\u5B89\u5168\u4F59\u88D5\u306E\u9593\u306F\u67A0\u3092\u9694\u96E2\u3057\u307E\u3059\u3002\u8A73\u7D30: ${detail}`);
36627
+ this.name = "CursorCleanupWarning";
36628
+ this.cause = cause;
36629
+ }
36630
+ };
36631
+
36632
+ // src/core/optimization/korderCursorExecutor.ts
36633
+ async function executeKorderCursor(input) {
36634
+ const handle = await input.client.openCursor({
36635
+ app: input.app,
36636
+ fields: input.fields.length > 0 ? input.fields : void 0,
36637
+ query: input.query,
36638
+ size: 500
36639
+ });
36640
+ const records = [];
36641
+ let seen = 0;
36642
+ let primaryError;
36643
+ let cleanupWarning;
36644
+ try {
36645
+ if (handle.totalCount > input.offset) {
36646
+ while (records.length < input.limit) {
36647
+ const page = await handle.nextPage();
36648
+ for (const record2 of page.records) {
36649
+ if (seen < input.offset) seen += 1;
36650
+ else if (records.length < input.limit) records.push(record2);
36651
+ else break;
36652
+ }
36653
+ if (!page.next) break;
36654
+ }
36655
+ }
36656
+ } catch (error51) {
36657
+ primaryError = error51;
36658
+ throw error51;
36659
+ } finally {
36660
+ try {
36661
+ await handle.close();
36662
+ } catch (cleanupError) {
36663
+ if (primaryError && primaryError instanceof Error) {
36664
+ Object.defineProperty(primaryError, "cursorCleanupError", {
36665
+ value: cleanupError,
36666
+ configurable: true
36667
+ });
36668
+ } else {
36669
+ cleanupWarning = new CursorCleanupWarning(cleanupError).message;
36670
+ }
36671
+ }
36672
+ }
36673
+ return { records, cleanupWarning };
36674
+ }
36675
+
36676
+ // src/converter/korderCursorQuery.ts
36677
+ function buildKorderCursorQuery(stmt) {
36678
+ const parts = [];
36679
+ if (stmt.where) parts.push(whereToKintone(stmt.where));
36680
+ const order = stmt.orderBy.map((item) => {
36681
+ if (item.key.type !== "FIELD_NAME") {
36682
+ throw new Error("ArgumentError: KORDER cursor key must be a direct field.");
36683
+ }
36684
+ return `${item.key.name} ${item.direction === "ASC" ? "asc" : "desc"}`;
36685
+ });
36686
+ parts.push(`order by ${order.join(", ")}`);
36687
+ return parts.join(" ");
36688
+ }
36689
+
36139
36690
  // src/engine/process.ts
36140
36691
  function flatten(record2, alias) {
36141
36692
  const row = {};
@@ -36200,9 +36751,9 @@ function applyJoin(leftRows, rightRows, join) {
36200
36751
  }
36201
36752
  return result;
36202
36753
  }
36203
- function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
36754
+ function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
36204
36755
  if (where === null) return rows;
36205
- return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
36756
+ return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
36206
36757
  }
36207
36758
  function hasAggregateColumns(columns) {
36208
36759
  return columns.some(
@@ -36263,7 +36814,7 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
36263
36814
  let strVal;
36264
36815
  if (arg.type === "FIELD_REF") {
36265
36816
  const raw = row[arg.field];
36266
- if (raw === void 0 || raw === "") continue;
36817
+ if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
36267
36818
  strVal = raw;
36268
36819
  } else {
36269
36820
  const n = evalArithExpr(arg, row);
@@ -36275,10 +36826,16 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
36275
36826
  const eff = distinct ? [...new Set(strValues)] : strValues;
36276
36827
  if (func === "COUNT") return eff.length;
36277
36828
  if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
36278
- const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
36279
- if (sortKind === "string") {
36280
- if (eff.length === 0) return "";
36281
- return func === "MAX" ? maxStringOf(eff) : minStringOf(eff);
36829
+ const comparison = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
36830
+ if (func === "MIN" || func === "MAX") {
36831
+ if (eff.length === 0) return 0;
36832
+ const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" ? syntheticSemantics("string") : syntheticSemantics("number"));
36833
+ let result = eff[0];
36834
+ for (const candidate of eff.slice(1)) {
36835
+ const cmp = compareCanonicalValues(candidate, result, semantics);
36836
+ if (func === "MAX" && cmp > 0 || func === "MIN" && cmp < 0) result = candidate;
36837
+ }
36838
+ return result;
36282
36839
  }
36283
36840
  const nums = eff.map(Number);
36284
36841
  switch (func) {
@@ -36286,37 +36843,12 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
36286
36843
  return nums.reduce((a, b) => a + b, 0);
36287
36844
  case "AVG":
36288
36845
  return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length;
36289
- // Math.max(...nums) は要素数が多いと RangeError になるためループで求める
36290
- case "MAX":
36291
- return nums.length === 0 ? 0 : maxOf(nums);
36292
- case "MIN":
36293
- return nums.length === 0 ? 0 : minOf(nums);
36294
36846
  }
36295
36847
  }
36296
36848
  function toAggregateFieldRef(field) {
36297
36849
  const dot = field.indexOf(".");
36298
36850
  return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
36299
36851
  }
36300
- function maxStringOf(values) {
36301
- let value = values[0];
36302
- for (const candidate of values) if (candidate > value) value = candidate;
36303
- return value;
36304
- }
36305
- function minStringOf(values) {
36306
- let value = values[0];
36307
- for (const candidate of values) if (candidate < value) value = candidate;
36308
- return value;
36309
- }
36310
- function maxOf(nums) {
36311
- let m = nums[0];
36312
- for (const n of nums) if (n > m) m = n;
36313
- return m;
36314
- }
36315
- function minOf(nums) {
36316
- let m = nums[0];
36317
- for (const n of nums) if (n < m) m = n;
36318
- return m;
36319
- }
36320
36852
  function evalAggArithExpr(node, rows, resolveAggSortKind) {
36321
36853
  if (node.type === "NUMBER") return node.value;
36322
36854
  if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
@@ -36348,9 +36880,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
36348
36880
  const argStr = aggregateArgLabel(arg);
36349
36881
  return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
36350
36882
  }
36351
- function applyHaving(rows, having, resolveFieldType) {
36883
+ function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
36352
36884
  if (having === null) return rows;
36353
- return rows.filter((row) => evalWhere(having, row, resolveFieldType));
36885
+ return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
36354
36886
  }
36355
36887
  function applyDistinct(rows, columns) {
36356
36888
  if (rows.length === 0) return rows;
@@ -36402,27 +36934,37 @@ function buildDistinctKeyBuilder(rows, columns) {
36402
36934
  return JSON.stringify(values);
36403
36935
  };
36404
36936
  }
36405
- function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
36937
+ function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
36406
36938
  if (orderBy.length === 0) return rows;
36407
- return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
36408
- }
36409
- function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
36410
- const keyMeta = orderBy.map(({ key }) => ({
36411
- orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
36412
- sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
36413
- }));
36939
+ return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2).rows.map((item) => item.row);
36940
+ }
36941
+ function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
36942
+ const keyMeta = orderBy.map(({ key }) => {
36943
+ if (key.type === "ARITH_KEY") return { semantics: syntheticSemantics("number") };
36944
+ if (key.type === "FUNC_KEY") {
36945
+ return { semantics: syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(key.expr.func) ? "number" : "string") };
36946
+ }
36947
+ const semantics = fieldSemantics2?.get(key.name);
36948
+ if (semantics) return { semantics };
36949
+ const orderMap = optionOrders?.get(key.name);
36950
+ if (orderMap) {
36951
+ return {
36952
+ semantics: {
36953
+ fieldType: "MULTI_SELECT",
36954
+ compareMode: "option",
36955
+ inSubtable: false,
36956
+ requiresCollectionOperators: false,
36957
+ optionOrder: orderMap
36958
+ }
36959
+ };
36960
+ }
36961
+ return { semantics: syntheticSemantics(sortKinds?.get(key.name) ?? "string") };
36962
+ });
36414
36963
  const decorated = rows.map((row) => ({
36415
36964
  row,
36416
36965
  keys: orderBy.map(({ key }, i) => {
36417
36966
  const s = evalOrderKey(key, row);
36418
- const n = Number(s);
36419
- const orderMap = keyMeta[i].orderMap;
36420
- return {
36421
- s,
36422
- n,
36423
- isNum: !Number.isNaN(n),
36424
- rank: orderMap ? minChoiceIndex(parseChoiceValues(s), orderMap) : 0
36425
- };
36967
+ return { s };
36426
36968
  })
36427
36969
  }));
36428
36970
  const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
@@ -36437,15 +36979,24 @@ function compareDecoratedRows(a, b, orderBy, keyMeta) {
36437
36979
  return 0;
36438
36980
  }
36439
36981
  function compareSortKeys(a, b, meta3) {
36440
- if (meta3.orderMap) {
36441
- if (a.rank !== b.rank) return a.rank - b.rank;
36442
- return a.s.localeCompare(b.s, "ja");
36443
- }
36444
- if (meta3.sortKind === "string") {
36445
- return a.s.localeCompare(b.s, "ja");
36446
- }
36447
- return a.isNum && b.isNum ? a.n - b.n : a.s.localeCompare(b.s, "ja");
36448
- }
36982
+ return compareCanonicalValues(a.s, b.s, meta3.semantics);
36983
+ }
36984
+ var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
36985
+ "LENGTH",
36986
+ "INSTR",
36987
+ "ROUND",
36988
+ "FLOOR",
36989
+ "CEIL",
36990
+ "TRUNCATE",
36991
+ "YEAR",
36992
+ "MONTH",
36993
+ "DAY",
36994
+ "DATEDIFF",
36995
+ "ABS",
36996
+ "MOD",
36997
+ "POWER",
36998
+ "SQRT"
36999
+ ]);
36449
37000
  function evalOrderKey(key, row) {
36450
37001
  switch (key.type) {
36451
37002
  case "FIELD_NAME":
@@ -36456,30 +37007,7 @@ function evalOrderKey(key, row) {
36456
37007
  return evalStringFunc(key.expr, row);
36457
37008
  }
36458
37009
  }
36459
- function parseChoiceValues(raw) {
36460
- const trimmed = raw.trim();
36461
- if (trimmed === "") return [""];
36462
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
36463
- try {
36464
- const arr = JSON.parse(trimmed);
36465
- if (Array.isArray(arr)) {
36466
- return arr.map((v) => String(v ?? ""));
36467
- }
36468
- } catch {
36469
- }
36470
- }
36471
- return [trimmed];
36472
- }
36473
- function minChoiceIndex(values, orderMap) {
36474
- let min = Number.MAX_SAFE_INTEGER;
36475
- for (const value of values) {
36476
- const idx = orderMap.get(value);
36477
- const rank = idx ?? Number.MAX_SAFE_INTEGER;
36478
- if (rank < min) min = rank;
36479
- }
36480
- return min;
36481
- }
36482
- function applyWindow(rows, columns, optionOrders, sortKinds) {
37010
+ function applyWindow(rows, columns, optionOrders, sortKinds, fieldSemantics2) {
36483
37011
  const windows = columns.filter((column) => column.type === "WINDOW_COL");
36484
37012
  if (rows.length === 0 || windows.length === 0) return rows;
36485
37013
  for (const window of windows) {
@@ -36491,7 +37019,7 @@ function applyWindow(rows, columns, optionOrders, sortKinds) {
36491
37019
  else partitions.set(key, [row]);
36492
37020
  }
36493
37021
  for (const partition of partitions.values()) {
36494
- const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
37022
+ const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
36495
37023
  const sorted = sortedResult.rows;
36496
37024
  let rank = 1;
36497
37025
  let denseRank = 1;
@@ -36516,7 +37044,7 @@ function applyLimit(rows, limit, offset) {
36516
37044
  if (limit === null) return rows.slice(start);
36517
37045
  return rows.slice(start, start + limit);
36518
37046
  }
36519
- function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
37047
+ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2) {
36520
37048
  if (columns.length === 1 && columns[0].type === "WILDCARD") {
36521
37049
  const projected2 = rows.map((row) => stripParentShortcutColumns(row));
36522
37050
  const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
@@ -36580,7 +37108,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
36580
37108
  }
36581
37109
  case "CASE_COL": {
36582
37110
  const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
36583
- out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
37111
+ out[key] = evalCaseWhen(col.expr, row, resolveFieldType, resolveFieldSemantics2);
36584
37112
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
36585
37113
  break;
36586
37114
  }
@@ -36735,6 +37263,26 @@ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
36735
37263
  args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
36736
37264
  };
36737
37265
  }
37266
+ function deriveOutputOrderSemantics(columns) {
37267
+ const result = /* @__PURE__ */ new Map();
37268
+ for (const column of columns) {
37269
+ if (!("alias" in column) || !column.alias) continue;
37270
+ if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
37271
+ result.set(column.alias, syntheticSemantics("number"));
37272
+ } else if (column.type === "AGGREGATE") {
37273
+ if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
37274
+ result.set(column.alias, syntheticSemantics("number"));
37275
+ } else if (column.func === "GROUP_CONCAT") {
37276
+ result.set(column.alias, syntheticSemantics("string"));
37277
+ }
37278
+ } else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
37279
+ result.set(column.alias, syntheticSemantics("string"));
37280
+ } else if (column.type === "STRFUNC_COL") {
37281
+ result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
37282
+ }
37283
+ }
37284
+ return result;
37285
+ }
36738
37286
  function runFullScan(input) {
36739
37287
  const {
36740
37288
  stmt,
@@ -36742,12 +37290,17 @@ function runFullScan(input) {
36742
37290
  scalarCache,
36743
37291
  optionOrders,
36744
37292
  sortKinds,
37293
+ orderSemantics,
36745
37294
  fieldTypeResolver,
37295
+ fieldSemanticsResolver,
36746
37296
  havingFieldTypeResolver,
37297
+ havingFieldSemanticsResolver,
36747
37298
  aggregateSortKindResolver,
36748
37299
  appliedKlikes,
36749
37300
  sourceColumns
36750
37301
  } = input;
37302
+ const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
37303
+ for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
36751
37304
  let rows = [];
36752
37305
  const mainAlias = stmt.from.alias;
36753
37306
  const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
@@ -36758,18 +37311,18 @@ function runFullScan(input) {
36758
37311
  const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
36759
37312
  rows = applyJoin(rows, rightRows, join);
36760
37313
  }
36761
- rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
37314
+ rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
36762
37315
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
36763
37316
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
36764
37317
  }
36765
- rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
36766
- rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
37318
+ rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
37319
+ rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
36767
37320
  if (stmt.distinct) {
36768
37321
  rows = applyDistinct(rows, stmt.columns);
36769
37322
  }
36770
- rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
37323
+ rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds, effectiveOrderSemantics);
36771
37324
  rows = applyLimit(rows, stmt.limit, stmt.offset);
36772
- return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
37325
+ return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns, fieldSemanticsResolver);
36773
37326
  }
36774
37327
 
36775
37328
  // src/converter/subtableAdapter.ts
@@ -37037,36 +37590,234 @@ function renderValidationValue(value) {
37037
37590
  return String(value);
37038
37591
  }
37039
37592
 
37040
- // src/execute.ts
37041
- 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";
37042
- var SearchAbortedError = class extends Error {
37043
- constructor() {
37044
- 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");
37045
- this.name = "SearchAbortedError";
37046
- }
37047
- };
37048
- var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
37049
- var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
37050
- async function execute(sql, client, options = {}) {
37051
- const startedAt = Date.now();
37052
- const stmt = parseSql(sql);
37053
- const metrics = createEmptyMetrics();
37054
- const countedClient = wrapClientWithMetrics(client, metrics);
37055
- const collector = { aborted: false };
37056
- const guardedClient = wrapClientWithSearchAbort(
37057
- countedClient,
37058
- collector,
37059
- !isSelectLikeStatement(stmt)
37060
- );
37061
- const result = await executeParsedStatement(
37062
- stmt,
37063
- guardedClient,
37064
- options,
37065
- options.cacheContext ?? "default"
37066
- );
37067
- metrics.elapsedMs = Date.now() - startedAt;
37068
- return { ...attachSearchAbortWarning(result, collector), metrics };
37069
- }
37593
+ // src/core/optimization/whereCapability.ts
37594
+ var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
37595
+ var EQUALITY_IN = ["=", "!=", "in", "not in"];
37596
+ var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
37597
+ ["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37598
+ ["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37599
+ ["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
37600
+ ["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
37601
+ ["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
37602
+ ["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
37603
+ ["DATE", new Set(RANGE_AND_EQUALITY)],
37604
+ ["TIME", new Set(RANGE_AND_EQUALITY)],
37605
+ ["DATETIME", new Set(RANGE_AND_EQUALITY)],
37606
+ ["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
37607
+ ["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
37608
+ ["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37609
+ ["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37610
+ ["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
37611
+ ["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
37612
+ ["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
37613
+ ["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
37614
+ ["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
37615
+ ["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37616
+ ["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
37617
+ ["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37618
+ ["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37619
+ ["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37620
+ ["STATUS", new Set(EQUALITY_IN)]
37621
+ ]);
37622
+ var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
37623
+ "RECORD_NUMBER",
37624
+ "__ID__",
37625
+ "CREATOR",
37626
+ "MODIFIER",
37627
+ "CREATED_TIME",
37628
+ "UPDATED_TIME",
37629
+ "DATE",
37630
+ "TIME",
37631
+ "DATETIME",
37632
+ "SINGLE_LINE_TEXT",
37633
+ "LINK",
37634
+ "NUMBER",
37635
+ "CALC",
37636
+ "MULTI_LINE_TEXT",
37637
+ "RICH_TEXT",
37638
+ "RADIO_BUTTON",
37639
+ "DROP_DOWN",
37640
+ "STATUS",
37641
+ // 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
37642
+ "KSQL_STRING",
37643
+ "KSQL_NUMBER",
37644
+ "KSQL_BOOLEAN"
37645
+ ]);
37646
+ var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
37647
+ "CHECK_BOX",
37648
+ "MULTI_SELECT",
37649
+ "FILE",
37650
+ "USER_SELECT",
37651
+ "ORGANIZATION_SELECT",
37652
+ "GROUP_SELECT",
37653
+ "STATUS_ASSIGNEE",
37654
+ "CATEGORY"
37655
+ ]);
37656
+ function nativeWhereOperatorsForType(fieldType) {
37657
+ return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
37658
+ }
37659
+ function classifyWhereCapability(where, resolveField2) {
37660
+ if (where === null) {
37661
+ return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
37662
+ }
37663
+ return classifyNode(where, resolveField2);
37664
+ }
37665
+ function classifyNode(where, resolveField2) {
37666
+ switch (where.type) {
37667
+ case "BINARY":
37668
+ return classifyBinary(where.op, where.left, where.right.type, resolveField2);
37669
+ case "NULL_CHECK":
37670
+ if (where.field.type !== "FIELD") return localExpression();
37671
+ return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
37672
+ case "EXISTS":
37673
+ return localExpression();
37674
+ case "GROUP":
37675
+ return classifyNode(where.expr, resolveField2);
37676
+ case "NOT": {
37677
+ const inner = classifyNode(where.expr, resolveField2);
37678
+ return inner.capability === "SUPERSET_PREFILTER" ? { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] } : inner;
37679
+ }
37680
+ case "LOGICAL": {
37681
+ const left = classifyNode(where.left, resolveField2);
37682
+ const right = classifyNode(where.right, resolveField2);
37683
+ return combineLogical(where.op, left, right);
37684
+ }
37685
+ }
37686
+ }
37687
+ function classifyBinary(op, left, rightType, resolveField2) {
37688
+ if (left.type !== "FIELD") return localExpression();
37689
+ const semantics = resolveField2(left);
37690
+ if (!semantics) {
37691
+ return unsupported("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
37692
+ }
37693
+ if (!hasLocalContract(semantics.fieldType, op)) {
37694
+ return unsupported("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, normalizeOperator(op));
37695
+ }
37696
+ const nativeOp = normalizeOperator(op);
37697
+ const native = nativeWhereOperatorsForType(semantics.fieldType);
37698
+ const rightCanPush = rightType === "STRING" || rightType === "NUMBER" || rightType === "IN_LIST" || rightType === "KINTONE_FUNC";
37699
+ const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
37700
+ const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
37701
+ if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
37702
+ return {
37703
+ capability: "EXACT_PUSHDOWN",
37704
+ reasons: [{
37705
+ code: "WHERE_EXACT",
37706
+ field: left.field,
37707
+ fieldType: semantics.fieldType,
37708
+ operator: nativeOp
37709
+ }]
37710
+ };
37711
+ }
37712
+ return {
37713
+ capability: "LOCAL_ONLY",
37714
+ reasons: [{
37715
+ code: "WHERE_RESIDUAL",
37716
+ field: left.field,
37717
+ fieldType: semantics.fieldType,
37718
+ operator: nativeOp
37719
+ }]
37720
+ };
37721
+ }
37722
+ function classifyLocalOnlyField(field, operator, resolveField2) {
37723
+ const semantics = resolveField2(field);
37724
+ if (!semantics) return unsupported("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
37725
+ if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
37726
+ return unsupported("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
37727
+ }
37728
+ return {
37729
+ capability: "LOCAL_ONLY",
37730
+ reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
37731
+ };
37732
+ }
37733
+ function hasLocalContract(fieldType, op) {
37734
+ if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
37735
+ if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
37736
+ return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
37737
+ }
37738
+ function normalizeOperator(op) {
37739
+ switch (op) {
37740
+ case "<>":
37741
+ return "!=";
37742
+ case "IN":
37743
+ return "in";
37744
+ case "NOT_IN":
37745
+ return "not in";
37746
+ case "LIKE":
37747
+ case "KLIKE":
37748
+ return "like";
37749
+ case "NOT_LIKE":
37750
+ case "NOT_KLIKE":
37751
+ return "not like";
37752
+ default:
37753
+ return op;
37754
+ }
37755
+ }
37756
+ function combineLogical(op, left, right) {
37757
+ const reasons = [...left.reasons, ...right.reasons];
37758
+ if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
37759
+ return { capability: "UNSUPPORTED", reasons };
37760
+ }
37761
+ if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
37762
+ return { capability: "EXACT_PUSHDOWN", reasons };
37763
+ }
37764
+ if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
37765
+ return {
37766
+ capability: "SUPERSET_PREFILTER",
37767
+ reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
37768
+ };
37769
+ }
37770
+ return { capability: "LOCAL_ONLY", reasons };
37771
+ }
37772
+ function localExpression() {
37773
+ return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
37774
+ }
37775
+ function unsupported(code, field, fieldType, operator) {
37776
+ return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
37777
+ }
37778
+
37779
+ // src/execute.ts
37780
+ 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";
37781
+ var SearchAbortedError = class extends Error {
37782
+ constructor() {
37783
+ 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");
37784
+ this.name = "SearchAbortedError";
37785
+ }
37786
+ };
37787
+ var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
37788
+ var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
37789
+ var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
37790
+ var nextDefaultCacheContextId = 1;
37791
+ function resolveCacheContext(client, explicit) {
37792
+ if (explicit) return explicit;
37793
+ let context = defaultCacheContextByClient.get(client);
37794
+ if (!context) {
37795
+ context = `client:${nextDefaultCacheContextId++}`;
37796
+ defaultCacheContextByClient.set(client, context);
37797
+ }
37798
+ return context;
37799
+ }
37800
+ async function execute(sql, client, options = {}) {
37801
+ const startedAt = Date.now();
37802
+ const cacheContext = resolveCacheContext(client, options.cacheContext);
37803
+ const stmt = parseSql(sql);
37804
+ const metrics = createEmptyMetrics();
37805
+ const countedClient = wrapClientWithMetrics(client, metrics);
37806
+ const collector = { aborted: false };
37807
+ const guardedClient = wrapClientWithSearchAbort(
37808
+ countedClient,
37809
+ collector,
37810
+ !isSelectLikeStatement(stmt)
37811
+ );
37812
+ const result = await executeParsedStatement(
37813
+ stmt,
37814
+ guardedClient,
37815
+ options,
37816
+ cacheContext
37817
+ );
37818
+ metrics.elapsedMs = Date.now() - startedAt;
37819
+ return { ...attachSearchAbortWarning(result, collector), metrics };
37820
+ }
37070
37821
  function createEmptyMetrics() {
37071
37822
  return {
37072
37823
  getCalls: 0,
@@ -37076,6 +37827,15 @@ function createEmptyMetrics() {
37076
37827
  fieldCalls: 0,
37077
37828
  appsCalls: 0,
37078
37829
  processStatusCalls: 0,
37830
+ cursorCreateCalls: 0,
37831
+ cursorGetCalls: 0,
37832
+ cursorDeleteCalls: 0,
37833
+ cursorRecordsScanned: 0,
37834
+ cursorActiveCurrent: 0,
37835
+ cursorActivePeak: 0,
37836
+ cursorCleanupFailures: 0,
37837
+ cursorCreateOutcomeUnknown: 0,
37838
+ cursorQuarantinedCurrent: 0,
37079
37839
  fetchedRows: 0,
37080
37840
  elapsedMs: 0
37081
37841
  };
@@ -37088,6 +37848,48 @@ function wrapClientWithMetrics(client, metrics) {
37088
37848
  metrics.fetchedRows += res.records.length;
37089
37849
  return res;
37090
37850
  },
37851
+ openCursor: async (params) => {
37852
+ metrics.cursorCreateCalls += 1;
37853
+ let handle;
37854
+ try {
37855
+ handle = await client.openCursor(params);
37856
+ } catch (error51) {
37857
+ if (error51 instanceof Error && error51.name === "CursorCreateOutcomeUnknownError") {
37858
+ metrics.cursorCreateOutcomeUnknown += 1;
37859
+ metrics.cursorQuarantinedCurrent += 1;
37860
+ }
37861
+ throw error51;
37862
+ }
37863
+ metrics.cursorActiveCurrent += 1;
37864
+ metrics.cursorActivePeak = Math.max(metrics.cursorActivePeak, metrics.cursorActiveCurrent);
37865
+ let released = false;
37866
+ const markReleased = () => {
37867
+ if (released) return;
37868
+ released = true;
37869
+ metrics.cursorActiveCurrent -= 1;
37870
+ };
37871
+ return {
37872
+ totalCount: handle.totalCount,
37873
+ nextPage: async () => {
37874
+ metrics.cursorGetCalls += 1;
37875
+ const page = await handle.nextPage();
37876
+ metrics.cursorRecordsScanned += page.records.length;
37877
+ if (!page.next) markReleased();
37878
+ return page;
37879
+ },
37880
+ close: async () => {
37881
+ if (!released) metrics.cursorDeleteCalls += 1;
37882
+ try {
37883
+ await handle.close();
37884
+ markReleased();
37885
+ } catch (error51) {
37886
+ metrics.cursorCleanupFailures += 1;
37887
+ metrics.cursorQuarantinedCurrent += 1;
37888
+ throw error51;
37889
+ }
37890
+ }
37891
+ };
37892
+ },
37091
37893
  postRecords: (params) => {
37092
37894
  metrics.postCalls += 1;
37093
37895
  return client.postRecords(params);
@@ -37127,6 +37929,37 @@ function wrapClientWithSearchAbort(client, collector, failClosed) {
37127
37929
  }
37128
37930
  };
37129
37931
  }
37932
+ function wrapClientWithCursorScope(client) {
37933
+ const active = /* @__PURE__ */ new Set();
37934
+ return {
37935
+ client: {
37936
+ ...client,
37937
+ openCursor: async (params) => {
37938
+ const handle = await client.openCursor(params);
37939
+ active.add(handle);
37940
+ const remove = () => active.delete(handle);
37941
+ return {
37942
+ totalCount: handle.totalCount,
37943
+ async nextPage() {
37944
+ const page = await handle.nextPage();
37945
+ if (!page.next) remove();
37946
+ return page;
37947
+ },
37948
+ async close() {
37949
+ try {
37950
+ await handle.close();
37951
+ } finally {
37952
+ remove();
37953
+ }
37954
+ }
37955
+ };
37956
+ }
37957
+ },
37958
+ closeActive: async () => {
37959
+ await Promise.all([...active].map((handle) => handle.close().catch(() => void 0)));
37960
+ }
37961
+ };
37962
+ }
37130
37963
  function isSelectLikeStatement(stmt) {
37131
37964
  return stmt.type === "SELECT" || stmt.type === "UNION" || stmt.type === "WITH";
37132
37965
  }
@@ -37177,7 +38010,13 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
37177
38010
  case "DESCRIBE":
37178
38011
  return executeDescribe(stmt, client, cacheContext);
37179
38012
  case "EXPLAIN":
37180
- return executeExplain(stmt);
38013
+ return executeExplain(
38014
+ stmt,
38015
+ client,
38016
+ cacheContext,
38017
+ options.maxRecords ?? 1e4,
38018
+ options.cursorMaxActive ?? 2
38019
+ );
37181
38020
  // 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
37182
38021
  case "CREATE_TEMP_TABLE":
37183
38022
  throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
@@ -37208,7 +38047,7 @@ function materializedColumnMetaEqual(left, right) {
37208
38047
  if (!left || !right || left.size !== right.size) return false;
37209
38048
  for (const [column, meta3] of left) {
37210
38049
  const candidate = right.get(column);
37211
- if (!candidate || candidate.sortKind !== meta3.sortKind || candidate.fieldType !== meta3.fieldType) return false;
38050
+ if (!candidate || candidate.sortKind !== meta3.sortKind || candidate.fieldType !== meta3.fieldType || !fieldSemanticsEqual(candidate.semantics, meta3.semantics)) return false;
37212
38051
  }
37213
38052
  return true;
37214
38053
  }
@@ -37239,7 +38078,7 @@ async function executeBatch(sql, client, options = {}) {
37239
38078
  const countedClient = wrapClientWithMetrics(client, metrics);
37240
38079
  const startedAt = Date.now();
37241
38080
  const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
37242
- const cacheContext = options.cacheContext ?? "default";
38081
+ const cacheContext = resolveCacheContext(client, options.cacheContext);
37243
38082
  const tempTables = /* @__PURE__ */ new Map();
37244
38083
  const variables = /* @__PURE__ */ new Map();
37245
38084
  const results = [];
@@ -37284,9 +38123,11 @@ async function executeBatch(sql, client, options = {}) {
37284
38123
  searchAbortCollector,
37285
38124
  info.statementType !== "SELECT" && info.statementType !== "UNION" && info.statementType !== "WITH"
37286
38125
  );
38126
+ const cursorScope = wrapClientWithCursorScope(statementClient);
37287
38127
  const outcome = await runWithDeadline(
37288
- executeBatchStatement(statements[i], info, statementClient, stmtOptions, cacheContext, tempTables, variables),
37289
- remaining
38128
+ executeBatchStatement(statements[i], info, cursorScope.client, stmtOptions, cacheContext, tempTables, variables),
38129
+ remaining,
38130
+ cursorScope.closeActive
37290
38131
  );
37291
38132
  if (outcome.result) {
37292
38133
  outcome.result = attachSearchAbortWarning(outcome.result, searchAbortCollector);
@@ -37333,7 +38174,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
37333
38174
  cacheContext,
37334
38175
  tempTables
37335
38176
  );
37336
- variables.set(stmt.name, { type: "string", value });
38177
+ const first = resolvedStmt2.expr.query.columns[0];
38178
+ const numeric = first?.type === "ARITH_COL" || first?.type === "ARITH_AGG_COL" || first?.type === "WINDOW_COL" || first?.type === "AGGREGATE" && (first.func === "COUNT" || first.func === "SUM" || first.func === "AVG");
38179
+ const numberValue = numeric ? Number(value) : Number.NaN;
38180
+ variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
37337
38181
  } catch (e) {
37338
38182
  if (e instanceof ScalarSubqueryError) {
37339
38183
  throw new Error(`ArgumentError: ${e.message}`);
@@ -37441,19 +38285,47 @@ async function runSelectLike(query, client, options, cacheContext, tempTables) {
37441
38285
  }
37442
38286
  return executeQueryWithCte(query, client, options, tempTables, cacheContext, true);
37443
38287
  }
37444
- async function runWithDeadline(work, remainingMs) {
38288
+ async function runWithDeadline(work, remainingMs, onTimeout) {
37445
38289
  if (remainingMs === null) return work;
37446
38290
  if (remainingMs <= 0) {
38291
+ if (onTimeout) await onTimeout();
37447
38292
  void work.catch(() => {
37448
38293
  });
37449
38294
  throw new BatchTimeoutError();
37450
38295
  }
37451
38296
  let timer;
38297
+ let timedOut = false;
38298
+ const guardedWork = work.then(
38299
+ (value) => timedOut ? new Promise(() => void 0) : value,
38300
+ (error51) => {
38301
+ if (timedOut) return new Promise(() => void 0);
38302
+ throw error51;
38303
+ }
38304
+ );
37452
38305
  try {
37453
38306
  return await Promise.race([
37454
- work,
38307
+ guardedWork,
37455
38308
  new Promise((_, reject) => {
37456
- timer = setTimeout(() => reject(new BatchTimeoutError()), remainingMs);
38309
+ timer = setTimeout(() => {
38310
+ timedOut = true;
38311
+ void (async () => {
38312
+ if (onTimeout) {
38313
+ let cleanupTimer;
38314
+ try {
38315
+ await Promise.race([
38316
+ onTimeout(),
38317
+ new Promise((resolve2) => {
38318
+ cleanupTimer = setTimeout(resolve2, 5e3);
38319
+ cleanupTimer.unref?.();
38320
+ })
38321
+ ]);
38322
+ } finally {
38323
+ if (cleanupTimer) clearTimeout(cleanupTimer);
38324
+ }
38325
+ }
38326
+ reject(new BatchTimeoutError());
38327
+ })();
38328
+ }, remainingMs);
37457
38329
  })
37458
38330
  ]);
37459
38331
  } catch (e) {
@@ -37564,13 +38436,14 @@ var ScalarSubqueryError = class extends Error {
37564
38436
  };
37565
38437
  async function executeAssert(stmt, client, options, cacheContext, tempTables) {
37566
38438
  const left = await evalAssertOperand(stmt.left, client, options, cacheContext, tempTables);
38439
+ const semantics = stmt.left.type === "NUMBER" || stmt.left.type === "ARITH" ? syntheticSemantics("number") : syntheticSemantics("string");
37567
38440
  if (stmt.op === "BETWEEN") {
37568
38441
  if (stmt.low === null || stmt.high === null) {
37569
38442
  throw new Error("ArgumentError: malformed ASSERT statement.");
37570
38443
  }
37571
38444
  const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
37572
38445
  const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
37573
- if (!compareScalarValues(">=", left, low) || !compareScalarValues("<=", left, high)) {
38446
+ if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
37574
38447
  throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
37575
38448
  }
37576
38449
  return { type: "ASSERT", condition: stmt.text };
@@ -37579,7 +38452,7 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
37579
38452
  throw new Error("ArgumentError: malformed ASSERT statement.");
37580
38453
  }
37581
38454
  const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
37582
- if (!compareScalarValues(stmt.op, left, right)) {
38455
+ if (!compareScalarValues(stmt.op, left, right, semantics)) {
37583
38456
  throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
37584
38457
  }
37585
38458
  return { type: "ASSERT", condition: stmt.text };
@@ -37652,6 +38525,149 @@ function evalAssertArith(node) {
37652
38525
  }
37653
38526
  throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
37654
38527
  }
38528
+ async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables, forcePhysicalMetadata = false) {
38529
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
38530
+ const physicalAppIds = forcePhysicalMetadata || whereNeedsFieldMetadata(stmt.where) ? [...new Set(tables.filter((table) => table.cteName === null).map((table) => table.appId))] : [];
38531
+ const infosByApp = new Map(
38532
+ await Promise.all(physicalAppIds.map(async (appId) => {
38533
+ const infos = await getFieldsCached(appId, client, cacheContext);
38534
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
38535
+ }))
38536
+ );
38537
+ const orderedFields = /* @__PURE__ */ new Set();
38538
+ const collectOrderedFields = (node) => {
38539
+ if (Array.isArray(node)) {
38540
+ node.forEach(collectOrderedFields);
38541
+ return;
38542
+ }
38543
+ if (node === null || typeof node !== "object") return;
38544
+ const value = node;
38545
+ if (value["type"] === "SELECT") return;
38546
+ if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
38547
+ const left = value["left"];
38548
+ if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
38549
+ orderedFields.add(left["field"]);
38550
+ }
38551
+ }
38552
+ Object.values(value).forEach(collectOrderedFields);
38553
+ };
38554
+ collectOrderedFields(stmt.where);
38555
+ collectOrderedFields(stmt.having);
38556
+ for (const column of stmt.columns) {
38557
+ if (column.type === "CASE_COL") collectOrderedFields(column.expr);
38558
+ }
38559
+ const statusOrdersByApp = /* @__PURE__ */ new Map();
38560
+ await Promise.all([...infosByApp].map(async ([appId, infos]) => {
38561
+ const needsStatus = [...orderedFields].some((field) => infos.get(field)?.fieldType === "STATUS");
38562
+ if (!needsStatus) return;
38563
+ const order = await loadProcessStatusOrder(appId, client, cacheContext);
38564
+ if (order) statusOrdersByApp.set(appId, order);
38565
+ }));
38566
+ const fromPhysical = (table, field) => {
38567
+ if (field === "$id") return withFieldSemanticSource(
38568
+ resolveFieldSemantics({ fieldType: "__ID__" }),
38569
+ table.appId,
38570
+ "$id"
38571
+ );
38572
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field));
38573
+ if (!info) return void 0;
38574
+ const base = info.semantics ?? resolveFieldSemantics(info);
38575
+ const semantics = info.fieldType === "STATUS" && statusOrdersByApp.has(table.appId) ? { ...base, optionOrder: statusOrdersByApp.get(table.appId) } : base;
38576
+ return withFieldSemanticSource(
38577
+ semantics,
38578
+ table.appId,
38579
+ info.code
38580
+ );
38581
+ };
38582
+ return (field) => {
38583
+ if (field.tableAlias !== null) {
38584
+ if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
38585
+ return fromPhysical(stmt.from, field.field);
38586
+ }
38587
+ const table = tables.find((candidate) => candidate.alias === field.tableAlias);
38588
+ if (!table) return void 0;
38589
+ if (table.cteName !== null) {
38590
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
38591
+ }
38592
+ return fromPhysical(table, field.field);
38593
+ }
38594
+ if (stmt.joins.length === 0) {
38595
+ if (stmt.from.cteName !== null) {
38596
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
38597
+ }
38598
+ return fromPhysical(stmt.from, field.field);
38599
+ }
38600
+ const matches = tables.flatMap((table) => {
38601
+ const semantics = table.cteName !== null ? materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics : fromPhysical(table, field.field);
38602
+ return semantics ? [semantics] : [];
38603
+ });
38604
+ if (matches.length === 1) return matches[0];
38605
+ return matches.length > 1 ? syntheticSemantics("string") : void 0;
38606
+ };
38607
+ }
38608
+ function selectCaseConditionsNeedFieldMetadata(stmt) {
38609
+ return stmt.columns.some((column) => column.type === "CASE_COL" && column.expr.branches.some((branch) => whereNeedsFieldMetadata(branch.condition)));
38610
+ }
38611
+ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
38612
+ const aliases = /* @__PURE__ */ new Map();
38613
+ for (const column of stmt.columns) {
38614
+ if (!("alias" in column) || !column.alias) continue;
38615
+ let semantics;
38616
+ if (column.type === "FIELD") semantics = rowResolver(aggregateFieldRef(column.field));
38617
+ else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
38618
+ semantics = syntheticSemantics("number");
38619
+ } else if (column.type === "AGGREGATE") {
38620
+ if (column.func === "MIN" || column.func === "MAX") {
38621
+ semantics = column.arg.type === "FIELD_REF" ? rowResolver(aggregateFieldRef(column.arg.field)) : syntheticSemantics("number");
38622
+ } else {
38623
+ semantics = column.func === "GROUP_CONCAT" ? syntheticSemantics("string") : syntheticSemantics("number");
38624
+ }
38625
+ } else if (column.type === "STRFUNC_COL") {
38626
+ semantics = stringFunctionColumnMeta(column.expr).semantics;
38627
+ } else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
38628
+ semantics = syntheticSemantics("string");
38629
+ }
38630
+ if (semantics) aliases.set(column.alias, semantics);
38631
+ }
38632
+ return (field) => field.tableAlias === null && aliases.has(field.field) ? aliases.get(field.field) : rowResolver(field);
38633
+ }
38634
+ async function resolveSelectWhereCapability(stmt, client, cacheContext, materializedTables) {
38635
+ if (stmt.where === null) return classifyWhereCapability(null, () => void 0);
38636
+ const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables);
38637
+ return classifyWhereCapability(stmt.where, resolver);
38638
+ }
38639
+ function formatWhereCapabilityFailure(result) {
38640
+ const reason = result.reasons.find(
38641
+ (candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED"
38642
+ ) ?? result.reasons[0];
38643
+ const details = [
38644
+ reason?.field ? `field=${reason.field}` : null,
38645
+ reason?.fieldType ? `type=${reason.fieldType}` : null,
38646
+ reason?.operator ? `operator=${reason.operator}` : null,
38647
+ reason?.code ? `reason=${reason.code}` : null
38648
+ ].filter((value) => value !== null).join(", ");
38649
+ return details || "reason=WHERE_UNSUPPORTED";
38650
+ }
38651
+ function hasCanonicalOrder(stmt) {
38652
+ return stmt.orderBy.length > 0 || stmt.columns.some(
38653
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
38654
+ );
38655
+ }
38656
+ async function assertDmlWhereCapability(stmt, client, cacheContext) {
38657
+ if (stmt.subtableCode || stmt.type === "UPDATE" && stmt.from != null) return;
38658
+ const fields = whereNeedsFieldMetadata(stmt.where) ? await getFieldsCached(stmt.appId, client, cacheContext) : [];
38659
+ const byCode = new Map(fields.map((field) => [field.code, field]));
38660
+ const result = classifyWhereCapability(stmt.where, (field) => {
38661
+ if (field.field === "$id") return resolveFieldSemantics({ fieldType: "__ID__" });
38662
+ const info = byCode.get(field.field);
38663
+ return info?.semantics ?? (info ? resolveFieldSemantics(info) : void 0);
38664
+ });
38665
+ if (result.capability !== "EXACT_PUSHDOWN") {
38666
+ throw new DmlConvertError(
38667
+ `WHERE predicate cannot be represented by kintone REST (${formatWhereCapabilityFailure(result)})`
38668
+ );
38669
+ }
38670
+ }
37655
38671
  async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
37656
38672
  let result;
37657
38673
  if (isNoFromSelect(stmt)) {
@@ -37662,12 +38678,51 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
37662
38678
  return result;
37663
38679
  }
37664
38680
  await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
37665
- const mode = resolveSelectMode(stmt);
37666
- await validateSelectFieldCodes(stmt, mode, client, cacheContext);
37667
- if (mode === "SIMPLE") {
37668
- result = await executeSimpleSelect(stmt, client, options, cacheContext);
37669
- } else {
37670
- result = await executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
38681
+ const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
38682
+ if (whereCapability.capability === "UNSUPPORTED") {
38683
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
38684
+ }
38685
+ const staticMode = resolveSelectMode(stmt);
38686
+ const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
38687
+ const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
38688
+ const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
38689
+ stmt,
38690
+ staticMode: mode,
38691
+ whereCapability: whereCapability.capability,
38692
+ orderSemantics: orderMeta.semantics,
38693
+ maxRecords: options.maxRecords ?? 1e4,
38694
+ hasKlike: whereHasKlike(stmt.where)
38695
+ }) : null;
38696
+ await validateSelectFieldCodes(
38697
+ stmt,
38698
+ orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : mode,
38699
+ client,
38700
+ cacheContext
38701
+ );
38702
+ const completeInputRequired = orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR" ? requiresCompleteInput({ ...stmt, orderBy: [] }) : requiresCompleteInput(stmt);
38703
+ const truncateWasDisabled = completeInputRequired && options.onLimitReached === "truncate";
38704
+ const effectiveOptions = truncateWasDisabled ? { ...options, onLimitReached: "error" } : options;
38705
+ try {
38706
+ if (mode === "SIMPLE") {
38707
+ result = await executeSimpleSelect(stmt, client, effectiveOptions, cacheContext, orderPlan, orderMeta);
38708
+ } else {
38709
+ result = await executeFullScanSelect(
38710
+ stmt,
38711
+ client,
38712
+ effectiveOptions,
38713
+ cacheContext,
38714
+ cteCache,
38715
+ whereCapability.capability === "EXACT_PUSHDOWN",
38716
+ orderMeta
38717
+ );
38718
+ }
38719
+ } catch (error51) {
38720
+ if (completeInputRequired && error51 instanceof FetchAllLimitError) {
38721
+ throw new FetchAllLimitError(
38722
+ "ORDER BY\u306E\u6B63\u3057\u3044\u7D50\u679C\u306B\u306F\u5B8C\u5168\u306A\u5019\u88DC\u96C6\u5408\u304C\u5FC5\u8981\u3067\u3059\u3002" + (truncateWasDisabled ? "onLimit=truncate\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002" : "") + error51.message
38723
+ );
38724
+ }
38725
+ throw error51;
37671
38726
  }
37672
38727
  if (captureColumnMeta) {
37673
38728
  materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
@@ -37728,19 +38783,41 @@ function executeNoFromSelect(stmt) {
37728
38783
  const rows = applyLimit(projected, stmt.limit, stmt.offset);
37729
38784
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
37730
38785
  }
37731
- async function executeSimpleSelect(stmt, client, options, cacheContext) {
37732
- const params = selectToKintoneParams(stmt);
38786
+ async function executeSimpleSelect(stmt, client, options, cacheContext, orderPlan, orderMeta) {
38787
+ const restStmt = orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt;
38788
+ const params = selectToKintoneParams(restStmt);
38789
+ const fetchFields = orderPlan?.kind === "CANONICAL_LOCAL" ? selectToFetchAllFields(stmt, stmt.from) : params.fields;
37733
38790
  const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
37734
38791
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
38792
+ const projectionSemanticsResolver = stmt.columns.some((column) => column.type === "CASE_COL") ? await buildWhereFieldSemanticsResolver(
38793
+ stmt,
38794
+ client,
38795
+ cacheContext,
38796
+ void 0,
38797
+ selectCaseConditionsNeedFieldMetadata(stmt)
38798
+ ) : void 0;
37735
38799
  const maxRecords2 = options.maxRecords ?? 1e4;
37736
38800
  const warnings = /* @__PURE__ */ new Set();
37737
38801
  const onLimit2 = options.onLimitReached ?? "error";
37738
38802
  const parallel = options.fetchParallel ?? 1;
37739
- const useSingleGet = stmt.limit !== null && stmt.limit <= 500;
38803
+ const useRestWindow = stmt.orderBy.length > 0 ? orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR" : stmt.limit !== null && stmt.limit <= 500;
37740
38804
  const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
37741
38805
  const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords2 && !whereHasKlike(stmt.where) ? needed : void 0;
37742
38806
  let records;
37743
- if (useSingleGet) {
38807
+ if ((orderPlan?.kind === "KORDER_NATIVE" || orderPlan?.kind === "KORDER_CURSOR") && stmt.limit === 0) {
38808
+ records = [];
38809
+ } else if (orderPlan?.kind === "KORDER_CURSOR") {
38810
+ const cursorResult = await executeKorderCursor({
38811
+ client,
38812
+ app: params.app,
38813
+ fields: params.fields,
38814
+ query: buildKorderCursorQuery(stmt),
38815
+ offset: stmt.offset ?? 0,
38816
+ limit: stmt.limit
38817
+ });
38818
+ records = cursorResult.records;
38819
+ if (cursorResult.cleanupWarning) warnings.add(cursorResult.cleanupWarning);
38820
+ } else if (useRestWindow) {
37744
38821
  const res = await client.getRecords({
37745
38822
  app: params.app,
37746
38823
  query: params.query,
@@ -37753,7 +38830,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
37753
38830
  client.getRecords,
37754
38831
  params.app,
37755
38832
  baseQuery,
37756
- params.fields,
38833
+ fetchFields,
37757
38834
  {
37758
38835
  parallel,
37759
38836
  maxRecords: maxRecords2,
@@ -37766,16 +38843,23 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
37766
38843
  );
37767
38844
  }
37768
38845
  let rows = records.map((r) => flatten(r, null));
37769
- if (!useSingleGet) {
37770
- const { optionOrders, sortKinds } = await buildOrderByMetaForSelect(stmt, client, cacheContext);
37771
- rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
38846
+ if (!useRestWindow) {
38847
+ rows = applyOrderBy(
38848
+ rows,
38849
+ stmt.orderBy,
38850
+ orderMeta.optionOrders,
38851
+ orderMeta.sortKinds,
38852
+ orderMeta.semantics
38853
+ );
37772
38854
  rows = applyLimit(rows, stmt.limit, stmt.offset);
37773
38855
  }
37774
38856
  const { rows: projected, columns } = project(
37775
38857
  rows,
37776
38858
  stmt.columns,
37777
38859
  void 0,
37778
- fieldTypeResolvers.row
38860
+ fieldTypeResolvers.row,
38861
+ void 0,
38862
+ projectionSemanticsResolver
37779
38863
  );
37780
38864
  return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
37781
38865
  }
@@ -37848,8 +38932,8 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
37848
38932
  const statusFields = collectCandidateFieldCodes(candidates).filter((fieldCode) => fieldTypes.get(fieldCode) === "STATUS");
37849
38933
  if (statusFields.length > 0) {
37850
38934
  const process4 = await getProcessStatusesCached(appId, client, cacheContext);
37851
- if (process4.enable && process4.states.length > 0) {
37852
- const states = new Set(process4.states);
38935
+ if (process4.enable && process4.states && process4.states.length > 0) {
38936
+ const states = new Set(process4.states.map((state) => state.name));
37853
38937
  for (const fieldCode of statusFields) fieldOptions.set(fieldCode, states);
37854
38938
  }
37855
38939
  }
@@ -38027,7 +39111,18 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
38027
39111
  return [appId, new Map(infos.map((info) => [info.code, info]))];
38028
39112
  }))
38029
39113
  );
39114
+ const statusOrdersByApp = /* @__PURE__ */ new Map();
39115
+ const aggregateFieldNames = new Set(refs.map((ref) => ref.field));
39116
+ await Promise.all([...fieldInfosByApp].map(async ([appId, infos]) => {
39117
+ if (![...aggregateFieldNames].some((field) => infos.get(field)?.fieldType === "STATUS")) return;
39118
+ const order = await loadProcessStatusOrder(appId, client, cacheContext);
39119
+ if (order) statusOrdersByApp.set(appId, order);
39120
+ }));
38030
39121
  const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
39122
+ const semanticsForInfo = (info, appId) => {
39123
+ const base = info.semantics ?? resolveFieldSemantics(info);
39124
+ return info.fieldType === "STATUS" && statusOrdersByApp.has(appId) ? { ...base, optionOrder: statusOrdersByApp.get(appId) } : base;
39125
+ };
38031
39126
  return (ref) => {
38032
39127
  let info;
38033
39128
  if (ref.tableAlias !== null) {
@@ -38037,40 +39132,130 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
38037
39132
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
38038
39133
  if (!table) return void 0;
38039
39134
  if (table.cteName !== null) {
38040
- return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.sortKind;
39135
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
38041
39136
  }
38042
39137
  info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
38043
39138
  }
38044
39139
  } else if (stmt.joins.length === 0) {
38045
39140
  if (stmt.from.cteName !== null) {
38046
- return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.sortKind;
39141
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
38047
39142
  }
38048
39143
  info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
38049
39144
  } else {
38050
39145
  const matches = tables.flatMap((table) => {
38051
39146
  if (table.cteName !== null) {
38052
39147
  const materialized = materializedTables?.get(table.cteName);
38053
- return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.sortKind] : [];
39148
+ return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string")] : [];
38054
39149
  }
38055
39150
  const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
38056
- return candidate ? [aggregateSortKind(candidate)] : [];
39151
+ return candidate ? [semanticsForInfo(candidate, table.appId)] : [];
38057
39152
  });
38058
39153
  if (matches.length !== 1) return void 0;
38059
39154
  return matches[0];
38060
39155
  }
38061
- return info ? aggregateSortKind(info) : void 0;
39156
+ if (!info) return void 0;
39157
+ const sourceTable = ref.tableAlias !== null ? tables.find((table) => table.alias === ref.tableAlias) : stmt.joins.length === 0 ? stmt.from : void 0;
39158
+ return semanticsForInfo(info, sourceTable?.appId ?? stmt.from.appId);
38062
39159
  };
38063
39160
  }
38064
39161
  function fieldCodeForTypeLookup(table, field) {
38065
39162
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
38066
39163
  return field;
38067
39164
  }
38068
- function materializedMetaFromFieldInfo(info) {
38069
- return { sortKind: aggregateSortKind(info), fieldType: info.fieldType };
39165
+ function materializedMetaFromFieldInfo(info, sourceAppId) {
39166
+ const semantics = info.semantics ?? resolveFieldSemantics(info);
39167
+ return {
39168
+ sortKind: aggregateSortKind(info),
39169
+ fieldType: info.fieldType,
39170
+ semantics: sourceAppId === void 0 ? semantics : withFieldSemanticSource(semantics, sourceAppId, info.code)
39171
+ };
39172
+ }
39173
+ function withCanonicalRestTie(stmt) {
39174
+ const hasId = stmt.orderBy.some(
39175
+ (item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
39176
+ );
39177
+ return hasId ? stmt : {
39178
+ ...stmt,
39179
+ orderBy: [...stmt.orderBy, { key: { type: "FIELD_NAME", name: "$id" }, direction: "ASC" }]
39180
+ };
39181
+ }
39182
+ function syntheticColumnMeta(compareMode) {
39183
+ return { sortKind: compareMode, semantics: syntheticSemantics(compareMode) };
39184
+ }
39185
+ function unknownStringColumnMeta() {
39186
+ return { semantics: syntheticSemantics("string", "KSQL_UNKNOWN") };
39187
+ }
39188
+ function unsupportedColumnMeta(fieldType = "KSQL_ARRAY") {
39189
+ return {
39190
+ semantics: { fieldType, compareMode: "unsupported", inSubtable: false, requiresCollectionOperators: false }
39191
+ };
39192
+ }
39193
+ function systemColumnMeta(field) {
39194
+ if (field === "$id" || field === "_rid" || field === "_pid") {
39195
+ return {
39196
+ sortKind: "number",
39197
+ fieldType: "__ID__",
39198
+ semantics: resolveFieldSemantics({ fieldType: "__ID__" })
39199
+ };
39200
+ }
39201
+ if (field === "$revision") return syntheticColumnMeta("number");
39202
+ return void 0;
39203
+ }
39204
+ var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
39205
+ "LENGTH",
39206
+ "INSTR",
39207
+ "ROUND",
39208
+ "FLOOR",
39209
+ "CEIL",
39210
+ "TRUNCATE",
39211
+ "YEAR",
39212
+ "MONTH",
39213
+ "DAY",
39214
+ "DATEDIFF",
39215
+ "ABS",
39216
+ "MOD",
39217
+ "POWER",
39218
+ "SQRT"
39219
+ ]);
39220
+ function stringFunctionColumnMeta(expr) {
39221
+ if (expr.func === "CAST") {
39222
+ const target = expr.args[1];
39223
+ return target?.type === "STRING" && target.value === "NUMBER" ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
39224
+ }
39225
+ return NUMBER_RETURNING_STRING_FUNCTIONS.has(expr.func) ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
39226
+ }
39227
+ function caseResultColumnMeta(result, resolveField2) {
39228
+ if (result.type === "STRING") return syntheticColumnMeta("string");
39229
+ if (result.type === "ARRAY") return unsupportedColumnMeta();
39230
+ if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
39231
+ if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
39232
+ const source = resolveField2(aggregateFieldRef(result.field));
39233
+ return source ?? unknownStringColumnMeta();
39234
+ }
39235
+ function mergeExpressionColumnMeta(candidates) {
39236
+ if (candidates.length === 0) return unknownStringColumnMeta();
39237
+ const first = candidates[0];
39238
+ const withoutSource = (semantics) => {
39239
+ if (!semantics) return void 0;
39240
+ const { source: _source, ...rest } = semantics;
39241
+ return rest;
39242
+ };
39243
+ if (candidates.every(
39244
+ (candidate) => candidate.sortKind === first.sortKind && candidate.fieldType === first.fieldType && fieldSemanticsEqual(withoutSource(candidate.semantics), withoutSource(first.semantics))
39245
+ )) {
39246
+ const sameSource = candidates.every(
39247
+ (candidate) => fieldSemanticsEqual(candidate.semantics, first.semantics)
39248
+ );
39249
+ return sameSource ? first : { ...first, semantics: withoutSource(first.semantics) };
39250
+ }
39251
+ if (candidates.some((candidate) => candidate.semantics?.compareMode === "unsupported")) {
39252
+ return unsupportedColumnMeta("KSQL_MIXED_UNSUPPORTED");
39253
+ }
39254
+ return unknownStringColumnMeta();
38070
39255
  }
38071
39256
  function selectNeedsSourceColumnMeta(stmt) {
38072
39257
  return stmt.columns.some(
38073
- (column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF"
39258
+ (column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "CASE_COL" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF"
38074
39259
  );
38075
39260
  }
38076
39261
  async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
@@ -38087,18 +39272,18 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
38087
39272
  if (ref.tableAlias !== null) {
38088
39273
  if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
38089
39274
  const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
38090
- return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
39275
+ return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
38091
39276
  }
38092
39277
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
38093
39278
  if (!table) return void 0;
38094
39279
  if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
38095
39280
  const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
38096
- return info ? materializedMetaFromFieldInfo(info) : void 0;
39281
+ return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
38097
39282
  }
38098
39283
  if (stmt.joins.length === 0) {
38099
39284
  if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
38100
39285
  const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
38101
- return info ? materializedMetaFromFieldInfo(info) : void 0;
39286
+ return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
38102
39287
  }
38103
39288
  const matches = tables.flatMap((table) => {
38104
39289
  if (table.cteName !== null) {
@@ -38107,7 +39292,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
38107
39292
  return [materialized.columnMeta?.get(ref.field)];
38108
39293
  }
38109
39294
  const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
38110
- return info ? [materializedMetaFromFieldInfo(info)] : [];
39295
+ const meta3 = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
39296
+ return meta3 ? [meta3] : [];
38111
39297
  });
38112
39298
  return matches.length === 1 ? matches[0] : void 0;
38113
39299
  };
@@ -38137,19 +39323,27 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
38137
39323
  meta3 = resolveField2(aggregateFieldRef(column.field));
38138
39324
  } else if (column.type === "AGGREGATE") {
38139
39325
  if (column.func === "GROUP_CONCAT") {
38140
- meta3 = { sortKind: "string" };
39326
+ meta3 = syntheticColumnMeta("string");
38141
39327
  } else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
38142
- meta3 = { sortKind: "number" };
39328
+ meta3 = syntheticColumnMeta("number");
38143
39329
  } else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
38144
39330
  const source = resolveField2(aggregateFieldRef(column.arg.field));
38145
- if (source?.sortKind) meta3 = { sortKind: source.sortKind };
39331
+ if (source) meta3 = source;
38146
39332
  }
38147
39333
  } else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
38148
- meta3 = { sortKind: "number" };
39334
+ meta3 = syntheticColumnMeta("number");
38149
39335
  } else if (column.type === "LITERAL_COL") {
38150
- meta3 = { sortKind: "string" };
39336
+ meta3 = syntheticColumnMeta("string");
39337
+ } else if (column.type === "STRFUNC_COL") {
39338
+ meta3 = stringFunctionColumnMeta(column.expr);
38151
39339
  } else if (column.type === "WINDOW_COL") {
38152
- meta3 = { sortKind: "number" };
39340
+ meta3 = syntheticColumnMeta("number");
39341
+ } else if (column.type === "CASE_COL") {
39342
+ const results = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
39343
+ if (column.expr.elseResult) results.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
39344
+ meta3 = mergeExpressionColumnMeta(results);
39345
+ } else if (column.type === "SCALAR_SUBQUERY_COL") {
39346
+ meta3 = unknownStringColumnMeta();
38153
39347
  }
38154
39348
  if (meta3) inferred.set(output, meta3);
38155
39349
  });
@@ -38163,7 +39357,8 @@ function mergeUnionColumnMeta(left, right) {
38163
39357
  const a = leftMeta?.get(column);
38164
39358
  const rightColumn = right.columns[index];
38165
39359
  const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
38166
- if (a && b && a.sortKind === b.sortKind && a.fieldType === b.fieldType) merged.set(column, a);
39360
+ if (a && b) merged.set(column, mergeExpressionColumnMeta([a, b]));
39361
+ else if (a || b) merged.set(column, unknownStringColumnMeta());
38167
39362
  });
38168
39363
  return merged;
38169
39364
  }
@@ -38200,7 +39395,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
38200
39395
  };
38201
39396
  return { row, having };
38202
39397
  }
38203
- async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
39398
+ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
38204
39399
  const maxRecords2 = options.maxRecords ?? 1e4;
38205
39400
  const warnings = /* @__PURE__ */ new Set();
38206
39401
  const parallel = options.fetchParallel ?? 1;
@@ -38214,6 +39409,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38214
39409
  loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
38215
39410
  ]);
38216
39411
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
39412
+ const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
39413
+ stmt,
39414
+ client,
39415
+ cacheContext,
39416
+ cteCache,
39417
+ whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
39418
+ );
39419
+ const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
38217
39420
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
38218
39421
  validateKlikePushdownPlan(pushdownPlan);
38219
39422
  const mainPushDown = pushdownPlan.mainCondition;
@@ -38227,7 +39430,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38227
39430
  true,
38228
39431
  options.onLimitReached ?? "error",
38229
39432
  warnings,
38230
- mainPushDown
39433
+ mainPushDown,
39434
+ allowOriginalWherePushdown
38231
39435
  );
38232
39436
  const parallelJoins = [];
38233
39437
  const onOptJoins = [];
@@ -38253,7 +39457,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38253
39457
  }
38254
39458
  }
38255
39459
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
38256
- const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
39460
+ const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
38257
39461
  scalarCachePromise.catch(() => {
38258
39462
  });
38259
39463
  orderByMetaPromise.catch(() => {
@@ -38290,15 +39494,18 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38290
39494
  tables.set(join.table.alias, joinRecords);
38291
39495
  }));
38292
39496
  const scalarCache = await scalarCachePromise;
38293
- const { optionOrders, sortKinds } = await orderByMetaPromise;
39497
+ const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
38294
39498
  const { rows, columns } = runFullScan({
38295
39499
  tables,
38296
39500
  stmt,
38297
39501
  scalarCache,
38298
39502
  optionOrders,
38299
39503
  sortKinds,
39504
+ orderSemantics: semantics,
38300
39505
  fieldTypeResolver: fieldTypeResolvers.row,
39506
+ fieldSemanticsResolver,
38301
39507
  havingFieldTypeResolver: fieldTypeResolvers.having,
39508
+ havingFieldSemanticsResolver,
38302
39509
  aggregateSortKindResolver,
38303
39510
  appliedKlikes: pushdownPlan.appliedKlikes
38304
39511
  });
@@ -38395,16 +39602,39 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38395
39602
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
38396
39603
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
38397
39604
  ]);
39605
+ const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
39606
+ if (whereCapability.capability === "UNSUPPORTED") {
39607
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
39608
+ }
39609
+ const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
39610
+ if (hasCanonicalOrder(stmt)) {
39611
+ (stmt.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
39612
+ stmt,
39613
+ staticMode: "FULL_SCAN",
39614
+ whereCapability: whereCapability.capability,
39615
+ orderSemantics: orderMeta.semantics,
39616
+ maxRecords: maxRecords2,
39617
+ hasKlike: whereHasKlike(stmt.where)
39618
+ });
39619
+ }
38398
39620
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
38399
39621
  loadTypedPushdownMeta(stmt, client, cacheContext),
38400
39622
  loadTypedInFieldTypes(stmt, client, cacheContext),
38401
39623
  loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
38402
39624
  ]);
38403
39625
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
39626
+ const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
39627
+ stmt,
39628
+ client,
39629
+ cacheContext,
39630
+ cteCache,
39631
+ whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
39632
+ );
39633
+ const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
38404
39634
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
38405
39635
  validateKlikePushdownPlan(pushdownPlan);
38406
39636
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
38407
- const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
39637
+ const orderByMetaPromise = Promise.resolve(orderMeta);
38408
39638
  scalarCachePromise.catch(() => {
38409
39639
  });
38410
39640
  orderByMetaPromise.catch(() => {
@@ -38423,7 +39653,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38423
39653
  true,
38424
39654
  options.onLimitReached ?? "error",
38425
39655
  warnings,
38426
- pushdownPlan.mainCondition
39656
+ pushdownPlan.mainCondition,
39657
+ whereCapability.capability === "EXACT_PUSHDOWN"
38427
39658
  );
38428
39659
  tables.set(stmt.from.alias, mainRecords);
38429
39660
  }
@@ -38460,7 +39691,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38460
39691
  });
38461
39692
  await Promise.all(joinFetches);
38462
39693
  const scalarCache = await scalarCachePromise;
38463
- const { optionOrders, sortKinds } = await orderByMetaPromise;
39694
+ const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
38464
39695
  const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
38465
39696
  const { rows, columns } = runFullScan({
38466
39697
  tables,
@@ -38468,8 +39699,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38468
39699
  scalarCache,
38469
39700
  optionOrders,
38470
39701
  sortKinds,
39702
+ orderSemantics: semantics,
38471
39703
  fieldTypeResolver: fieldTypeResolvers.row,
39704
+ fieldSemanticsResolver,
38472
39705
  havingFieldTypeResolver: fieldTypeResolvers.having,
39706
+ havingFieldSemanticsResolver,
38473
39707
  aggregateSortKindResolver,
38474
39708
  appliedKlikes: pushdownPlan.appliedKlikes,
38475
39709
  sourceColumns
@@ -38481,13 +39715,13 @@ function processRowToKintoneRecord(row) {
38481
39715
  Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
38482
39716
  );
38483
39717
  }
38484
- async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords2, parallel, isMainTable, onLimit2, warnings, pushDownCond = null) {
39718
+ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords2, parallel, isMainTable, onLimit2, warnings, pushDownCond = null, allowOriginalWherePushdown = true) {
38485
39719
  const fields = selectToFetchAllFields(stmt, table);
38486
39720
  const onTruncate = (max) => {
38487
39721
  warnings.add(`\u53D6\u5F97\u4E0A\u9650\uFF08${max} \u4EF6\uFF09\u306B\u9054\u3057\u305F\u305F\u3081\u3001${max} \u4EF6\u3067\u6253\u3061\u5207\u3063\u3066\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002`);
38488
39722
  };
38489
39723
  if (!table.subtableCode) {
38490
- const baseQuery = isMainTable ? selectToFetchAllParams(stmt, table.appId).query : "";
39724
+ const baseQuery = isMainTable && allowOriginalWherePushdown ? selectToFetchAllParams(stmt, table.appId).query : "";
38491
39725
  const pushQuery = pushDownCond !== null ? whereToKintone(pushDownCond) : "";
38492
39726
  const query = baseQuery && pushQuery ? `(${baseQuery}) and (${pushQuery})` : baseQuery || pushQuery;
38493
39727
  const resolved = await fetchRecordsForSharedPlan(client.getRecords, table.appId, query, fields, {
@@ -38691,6 +39925,10 @@ async function getProcessStatusesCached(appId, client, cacheContext) {
38691
39925
  setScopedCacheValue(processStatusCache, cacheContext, appId, loading);
38692
39926
  return loading;
38693
39927
  }
39928
+ async function loadProcessStatusOrder(appId, client, cacheContext) {
39929
+ const process4 = await getProcessStatusesCached(appId, client, cacheContext);
39930
+ return process4.enable && process4.states !== null ? new Map(process4.states.map((state) => [state.name, state.index])) : void 0;
39931
+ }
38694
39932
  async function getFieldTypeMap(appId, client, cacheContext) {
38695
39933
  const cached2 = getScopedCacheValue(fieldTypeCache, cacheContext, appId);
38696
39934
  if (cached2) return cached2;
@@ -38734,18 +39972,118 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
38734
39972
  setScopedCacheValue(sortKindCache, cacheContext, appId, map2);
38735
39973
  return map2;
38736
39974
  }
38737
- async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
39975
+ function orderByFieldNames(stmt) {
39976
+ const items = [
39977
+ ...stmt.orderBy,
39978
+ ...stmt.columns.flatMap((column) => column.type === "WINDOW_COL" ? column.orderBy : [])
39979
+ ];
39980
+ return [...new Set(items.flatMap(
39981
+ (item) => item.key.type === "FIELD_NAME" ? [item.key.name] : []
39982
+ ))];
39983
+ }
39984
+ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables) {
39985
+ const names = orderByFieldNames(stmt);
39986
+ if (names.length === 0) return /* @__PURE__ */ new Map();
39987
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
39988
+ const ambiguousFields = /* @__PURE__ */ new Set();
39989
+ const infosByApp = new Map(
39990
+ await Promise.all([...new Set(
39991
+ tables.filter((table) => table.cteName === null).map((table) => table.appId)
39992
+ )].map(async (appId) => {
39993
+ const infos = await getFieldsCached(appId, client, cacheContext);
39994
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
39995
+ }))
39996
+ );
39997
+ const resolveField2 = (ref) => {
39998
+ if (ref.tableAlias !== null) {
39999
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
40000
+ const info2 = infosByApp.get(stmt.from.appId)?.get(ref.field);
40001
+ return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
40002
+ }
40003
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
40004
+ if (!table) return void 0;
40005
+ if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
40006
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
40007
+ return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
40008
+ }
40009
+ if (stmt.joins.length === 0) {
40010
+ if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
40011
+ const info = infosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
40012
+ return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
40013
+ }
40014
+ const matches = tables.flatMap((table) => {
40015
+ if (table.cteName !== null) {
40016
+ const materialized = materializedTables?.get(table.cteName);
40017
+ const meta4 = materialized?.columns.includes(ref.field) ? materialized.columnMeta?.get(ref.field) : void 0;
40018
+ return meta4 ? [meta4] : [];
40019
+ }
40020
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
40021
+ const meta3 = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
40022
+ return meta3 ? [meta3] : [];
40023
+ });
40024
+ if (matches.length > 1) ambiguousFields.add(ref.field);
40025
+ return matches.length === 1 ? matches[0] : void 0;
40026
+ };
40027
+ const aliasSemantics = /* @__PURE__ */ new Map();
40028
+ for (const column of stmt.columns) {
40029
+ if (!("alias" in column) || !column.alias) continue;
40030
+ let meta3;
40031
+ if (column.type === "FIELD") meta3 = resolveField2(aggregateFieldRef(column.field));
40032
+ else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
40033
+ meta3 = syntheticColumnMeta("number");
40034
+ } else if (column.type === "LITERAL_COL") meta3 = syntheticColumnMeta("string");
40035
+ else if (column.type === "STRFUNC_COL") meta3 = stringFunctionColumnMeta(column.expr);
40036
+ else if (column.type === "SCALAR_SUBQUERY_COL") meta3 = unknownStringColumnMeta();
40037
+ else if (column.type === "CASE_COL") {
40038
+ const candidates = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
40039
+ if (column.expr.elseResult) candidates.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
40040
+ meta3 = mergeExpressionColumnMeta(candidates);
40041
+ } else if (column.type === "AGGREGATE") {
40042
+ if (column.func === "MIN" || column.func === "MAX") {
40043
+ meta3 = column.arg.type === "FIELD_REF" ? resolveField2(aggregateFieldRef(column.arg.field)) : syntheticColumnMeta("number");
40044
+ } else {
40045
+ meta3 = column.func === "GROUP_CONCAT" ? syntheticColumnMeta("string") : syntheticColumnMeta("number");
40046
+ }
40047
+ }
40048
+ if (meta3?.semantics) aliasSemantics.set(column.alias, meta3.semantics);
40049
+ }
40050
+ const result = /* @__PURE__ */ new Map();
40051
+ for (const name of names) {
40052
+ const base = aliasSemantics.get(name) ?? resolveField2(aggregateFieldRef(name))?.semantics;
40053
+ if (!base) {
40054
+ const ref = aggregateFieldRef(name);
40055
+ if (ref.tableAlias === null && ambiguousFields.has(ref.field)) {
40056
+ result.set(name, resolveFieldSemantics({ fieldType: "KSQL_AMBIGUOUS" }));
40057
+ }
40058
+ continue;
40059
+ }
40060
+ let semantics = base;
40061
+ if (base.fieldType === "STATUS" && base.source && stmt.orderMode !== "KINTONE_NATIVE") {
40062
+ const process4 = await getProcessStatusesCached(base.source.appId, client, cacheContext);
40063
+ if (process4.enable && process4.states !== null) {
40064
+ semantics = {
40065
+ ...base,
40066
+ optionOrder: new Map(process4.states.map((state) => [state.name, state.index]))
40067
+ };
40068
+ }
40069
+ }
40070
+ result.set(name, semantics);
40071
+ }
40072
+ return result;
40073
+ }
40074
+ async function buildOrderByMetaForSelect(stmt, client, cacheContext, materializedTables) {
38738
40075
  const hasWindowOrderBy = stmt.columns.some(
38739
40076
  (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
38740
40077
  );
38741
40078
  if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
38742
- return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
40079
+ return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map(), semantics: /* @__PURE__ */ new Map() };
38743
40080
  }
38744
- const [optionOrders, sortKinds] = await Promise.all([
40081
+ const [optionOrders, sortKinds, semantics] = await Promise.all([
38745
40082
  buildOptionOrdersForSelect(stmt, client, cacheContext),
38746
- buildSortKindsForSelect(stmt, client, cacheContext)
40083
+ buildSortKindsForSelect(stmt, client, cacheContext),
40084
+ buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables)
38747
40085
  ]);
38748
- return { optionOrders, sortKinds };
40086
+ return { optionOrders, sortKinds, semantics };
38749
40087
  }
38750
40088
  async function buildOptionOrdersForSelect(stmt, client, cacheContext) {
38751
40089
  const optionOrders = /* @__PURE__ */ new Map();
@@ -38843,6 +40181,9 @@ var RejectLimitExceededError = class extends Error {
38843
40181
  }
38844
40182
  };
38845
40183
  async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
40184
+ if (stmt.type === "UPDATE") {
40185
+ await assertDmlWhereCapability(stmt, client, cacheContext);
40186
+ }
38846
40187
  const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
38847
40188
  const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
38848
40189
  if (new Set(payloadFields).size !== payloadFields.length) {
@@ -38882,18 +40223,22 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
38882
40223
  const columnMeta = /* @__PURE__ */ new Map();
38883
40224
  for (const column of payloadFields) {
38884
40225
  if (column === "$id") {
38885
- columnMeta.set(column, { sortKind: "number", fieldType: "RECORD_NUMBER" });
40226
+ columnMeta.set(column, {
40227
+ sortKind: "number",
40228
+ fieldType: "RECORD_NUMBER",
40229
+ semantics: resolveFieldSemantics({ fieldType: "RECORD_NUMBER" })
40230
+ });
38886
40231
  continue;
38887
40232
  }
38888
40233
  const info = infoByCode.get(column);
38889
- if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
38890
- }
38891
- columnMeta.set("$err_statement", { sortKind: "number" });
38892
- columnMeta.set("$err_operation", { sortKind: "string" });
38893
- columnMeta.set("$err_row", { sortKind: "number" });
38894
- columnMeta.set("$err_field", { sortKind: "string" });
38895
- columnMeta.set("$err_code", { sortKind: "string" });
38896
- columnMeta.set("$err_message", { sortKind: "string" });
40234
+ if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info, stmt.appId));
40235
+ }
40236
+ columnMeta.set("$err_statement", syntheticColumnMeta("number"));
40237
+ columnMeta.set("$err_operation", syntheticColumnMeta("string"));
40238
+ columnMeta.set("$err_row", syntheticColumnMeta("number"));
40239
+ columnMeta.set("$err_field", syntheticColumnMeta("string"));
40240
+ columnMeta.set("$err_code", syntheticColumnMeta("string"));
40241
+ columnMeta.set("$err_message", syntheticColumnMeta("string"));
38897
40242
  materializedMetaByValidationResult.set(result, columnMeta);
38898
40243
  return { result, candidates, invalidRowNumbers, columnMeta };
38899
40244
  }
@@ -39279,6 +40624,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
39279
40624
  };
39280
40625
  }
39281
40626
  async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
40627
+ await assertDmlWhereCapability(stmt, client, cacheContext);
39282
40628
  if (stmt.subtableCode) {
39283
40629
  return executeUpdateSubtable(stmt, client, options, cacheContext);
39284
40630
  }
@@ -39356,6 +40702,7 @@ function collectUpdateFromTargetFields(stmt) {
39356
40702
  return [...fields];
39357
40703
  }
39358
40704
  async function executeDelete(stmt, client, options, cacheContext) {
40705
+ await assertDmlWhereCapability(stmt, client, cacheContext);
39359
40706
  if (stmt.subtableCode) {
39360
40707
  return executeDeleteSubtable(stmt, client, options, cacheContext);
39361
40708
  }
@@ -39728,6 +41075,18 @@ async function executeReorder(stmt, client, options, cacheContext) {
39728
41075
  client,
39729
41076
  cacheContext
39730
41077
  );
41078
+ const reorderFields = await getFieldsCached(stmt.appId, client, cacheContext);
41079
+ const reorderSemanticsByCode = new Map(reorderFields.map((field) => [
41080
+ field.code,
41081
+ field.semantics ?? resolveFieldSemantics(field)
41082
+ ]));
41083
+ const resolveReorderSemantics = (field) => {
41084
+ if (field.field === "_idx" || field.field === "_pid" || field.field === "_rid") {
41085
+ return syntheticSemantics("number");
41086
+ }
41087
+ const code = field.field.startsWith("_p.") ? field.field.slice(3) : field.field;
41088
+ return reorderSemanticsByCode.get(code) ?? syntheticSemantics("string");
41089
+ };
39731
41090
  const parents = await fetchAll(
39732
41091
  client.getRecords,
39733
41092
  stmt.appId,
@@ -39736,7 +41095,13 @@ async function executeReorder(stmt, client, options, cacheContext) {
39736
41095
  { maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
39737
41096
  );
39738
41097
  const expanded = expandRowsForSubtableDml(parents, stmt.subtableCode);
39739
- const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(stmt.where, r.flat, resolveFieldType)).map((r) => r.parentId));
41098
+ const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(
41099
+ stmt.where,
41100
+ r.flat,
41101
+ resolveFieldType,
41102
+ void 0,
41103
+ resolveReorderSemantics
41104
+ )).map((r) => r.parentId));
39740
41105
  if (options.confirm) {
39741
41106
  const ok = await options.confirm(targetParentIds.size, "UPDATE");
39742
41107
  if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
@@ -39747,7 +41112,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
39747
41112
  if (!parent) continue;
39748
41113
  const rows = getMutableTableRows(parent, stmt.subtableCode);
39749
41114
  const sortable = rows.map((row, i) => ({ row, i, flat: buildFlatRowForSort(parent, stmt.subtableCode, row, i) }));
39750
- sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by));
41115
+ sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by, resolveReorderSemantics));
39751
41116
  const orderedRowIds = sortable.map((x) => x.row.id ?? "");
39752
41117
  await client.putRecords(buildSubtableReorderPutParams(stmt.appId, pid, getRevision(parent), stmt.subtableCode, orderedRowIds));
39753
41118
  }
@@ -39768,14 +41133,12 @@ function buildFlatRowForSort(parent, subtableCode, row, idx) {
39768
41133
  }
39769
41134
  return flat;
39770
41135
  }
39771
- function compareByOrder(a, b, orderBy) {
41136
+ function compareByOrder(a, b, orderBy, resolveSemantics) {
39772
41137
  for (const item of orderBy) {
39773
41138
  const av = evalOrderKeyForRow(item.key, a);
39774
41139
  const bv = evalOrderKeyForRow(item.key, b);
39775
- const an = Number(av);
39776
- const bn = Number(bv);
39777
- const numeric = !Number.isNaN(an) && !Number.isNaN(bn);
39778
- const cmp = numeric ? an - bn : av.localeCompare(bv, "ja");
41140
+ const semantics = item.key.type === "FIELD_NAME" ? resolveSemantics(aggregateFieldRef(item.key.name)) : item.key.type === "ARITH_KEY" ? syntheticSemantics("number") : stringFunctionColumnMeta(item.key.expr).semantics ?? syntheticSemantics("string");
41141
+ const cmp = compareCanonicalValues(av, bv, semantics ?? syntheticSemantics("string"));
39779
41142
  if (cmp !== 0) return item.direction === "ASC" ? cmp : -cmp;
39780
41143
  }
39781
41144
  return 0;
@@ -39972,35 +41335,140 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
39972
41335
  pending.forEach(([i], idx) => cache.set(i, values[idx]));
39973
41336
  return cache;
39974
41337
  }
39975
- function buildBatchExplainPlans(sql, injectedVariables) {
41338
+ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords2 = 1e4) {
41339
+ const fieldApps = /* @__PURE__ */ new Set();
41340
+ const processStatusApps = /* @__PURE__ */ new Set();
41341
+ const tracedClient = {
41342
+ ...client,
41343
+ getFields: async (appId) => {
41344
+ fieldApps.add(appId);
41345
+ return client.getFields(appId);
41346
+ },
41347
+ getProcessStatuses: async (appId) => {
41348
+ processStatusApps.add(appId);
41349
+ return client.getProcessStatuses(appId);
41350
+ }
41351
+ };
41352
+ const capabilities = /* @__PURE__ */ new Map();
41353
+ const orderPlans = /* @__PURE__ */ new Map();
41354
+ const seen = /* @__PURE__ */ new Set();
41355
+ const visit = async (node) => {
41356
+ if (node === null || typeof node !== "object") return;
41357
+ if (seen.has(node)) return;
41358
+ seen.add(node);
41359
+ if (Array.isArray(node)) {
41360
+ await Promise.all(node.map(visit));
41361
+ return;
41362
+ }
41363
+ const typed = node;
41364
+ if (typed["type"] === "SELECT") {
41365
+ const select = node;
41366
+ const physicalApps = [select.from, ...select.joins.map((join) => join.table)].filter((table) => table.cteName === null).map((table) => table.appId);
41367
+ const needsWhereSchema = whereNeedsFieldMetadata(select.where);
41368
+ if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
41369
+ physicalApps.forEach((appId) => fieldApps.add(appId));
41370
+ }
41371
+ const capability = await resolveSelectWhereCapability(select, tracedClient, cacheContext);
41372
+ if (capability.capability === "UNSUPPORTED") {
41373
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
41374
+ }
41375
+ capabilities.set(select, capability);
41376
+ if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
41377
+ const meta3 = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
41378
+ if (select.orderMode !== "KINTONE_NATIVE") {
41379
+ for (const semantics of meta3.semantics.values()) {
41380
+ if (semantics.fieldType === "STATUS" && semantics.source) {
41381
+ processStatusApps.add(semantics.source.appId);
41382
+ }
41383
+ }
41384
+ }
41385
+ const hasUnmaterializedSource = [select.from, ...select.joins.map((join) => join.table)].some((table) => table.cteName !== null);
41386
+ if (hasCanonicalOrder(select) && !hasUnmaterializedSource) {
41387
+ const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
41388
+ orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
41389
+ stmt: select,
41390
+ staticMode: mode,
41391
+ whereCapability: capability.capability,
41392
+ orderSemantics: meta3.semantics,
41393
+ maxRecords: maxRecords2,
41394
+ hasKlike: whereHasKlike(select.where)
41395
+ }));
41396
+ }
41397
+ }
41398
+ } else if (typed["type"] === "UPDATE" || typed["type"] === "DELETE") {
41399
+ fieldApps.add(node.appId);
41400
+ await assertDmlWhereCapability(
41401
+ node,
41402
+ tracedClient,
41403
+ cacheContext
41404
+ );
41405
+ }
41406
+ await Promise.all(Object.values(typed).map(visit));
41407
+ };
41408
+ await visit(query);
41409
+ if (typeof query === "object" && query !== null && query.type === "WITH" && canInlineSingleCte(query)) {
41410
+ const inlined = buildInlinedQuery(query);
41411
+ const capability = await resolveSelectWhereCapability(inlined, tracedClient, cacheContext);
41412
+ if (capability.capability === "UNSUPPORTED") {
41413
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
41414
+ }
41415
+ capabilities.set(inlined, capability);
41416
+ if (hasCanonicalOrder(inlined)) {
41417
+ const meta3 = await buildOrderByMetaForSelect(inlined, tracedClient, cacheContext);
41418
+ orderPlans.set(inlined, (inlined.orderMode === "KINTONE_NATIVE" ? planKorder : planCanonicalOrder)({
41419
+ stmt: inlined,
41420
+ staticMode: capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(inlined) : "FULL_SCAN",
41421
+ whereCapability: capability.capability,
41422
+ orderSemantics: meta3.semantics,
41423
+ maxRecords: maxRecords2,
41424
+ hasKlike: whereHasKlike(inlined.where)
41425
+ }));
41426
+ }
41427
+ }
41428
+ return { capabilities, orderPlans, fieldApps, processStatusApps };
41429
+ }
41430
+ function explainMetadataLines(analysis) {
41431
+ return [
41432
+ ...[...analysis.fieldApps].sort((a, b) => a - b).map((appId) => ` metadata API: form definition APP${appId}`),
41433
+ ...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`)
41434
+ ];
41435
+ }
41436
+ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords2 = 1e4, cursorMaxActive2 = 2) {
39976
41437
  const statements = parseSqlBatch(sql);
39977
41438
  const analysis = analyzeBatch(statements);
39978
41439
  validateDeclaredBatchVariables(statements, injectedVariables);
39979
41440
  const variables = /* @__PURE__ */ new Map();
39980
- return {
39981
- statementCount: statements.length,
39982
- statements: statements.map((stmt, i) => {
39983
- const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
39984
- validateKlikeStatement(planStmt);
39985
- const result = {
39986
- index: i,
39987
- type: analysis.statements[i].statementType,
39988
- plan: buildBatchStatementPlan(planStmt, analysis.statements[i])
39989
- };
39990
- if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
39991
- variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
39992
- }
39993
- return result;
39994
- })
39995
- };
41441
+ const plans = [];
41442
+ for (let i = 0; i < statements.length; i++) {
41443
+ const stmt = statements[i];
41444
+ const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
41445
+ validateKlikeStatement(planStmt);
41446
+ const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords2);
41447
+ const statementPlan = addCursorConcurrency(buildBatchStatementPlan(
41448
+ planStmt,
41449
+ analysis.statements[i],
41450
+ whereAnalysis.capabilities,
41451
+ whereAnalysis.orderPlans
41452
+ ), cursorMaxActive2);
41453
+ const metadataPlan = explainMetadataLines(whereAnalysis);
41454
+ plans.push({
41455
+ index: i,
41456
+ type: analysis.statements[i].statementType,
41457
+ plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
41458
+ });
41459
+ if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
41460
+ variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
41461
+ }
41462
+ }
41463
+ return { statementCount: statements.length, statements: plans };
39996
41464
  }
39997
- function buildBatchStatementPlan(stmt, info) {
41465
+ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans) {
39998
41466
  if (stmt.type === "CREATE_TEMP_TABLE") {
39999
41467
  return [
40000
41468
  `CREATE TEMP TABLE ${stmt.name}`,
40001
41469
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
40002
41470
  ` rows: \u5B9F\u4F53\u5316\u524D\u306E\u305F\u3081\u4E0D\u660E\uFF08\u65E2\u5B9A\u4E0A\u9650 ${TEMP_TABLE_MAX_ROWS} \u884C\u3001tempTableMaxRows \u3067\u5909\u66F4\u53EF\u3001\u8D85\u904E\u306F\u30A8\u30E9\u30FC\uFF09`,
40003
- ...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
41471
+ ...buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans).map((l) => ` ${l}`)
40004
41472
  ];
40005
41473
  }
40006
41474
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -40016,7 +41484,7 @@ function buildBatchStatementPlan(stmt, info) {
40016
41484
  `SET @${stmt.name} = (SELECT ...)`,
40017
41485
  " value: \u30B5\u30D6\u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u6642\u306B1\u56DE\u8A55\u4FA1\uFF081\u884C1\u5217\u30FB\u30D0\u30C3\u30C1\u5185\u5B9A\u6570\u30FB\u7D50\u679C\u30E1\u30BF\u30C7\u30FC\u30BF\u306B\u306F\u975E\u516C\u958B\uFF09",
40018
41486
  " subquery:",
40019
- ...buildPlanForBatchQuery(stmt.expr.query, subInfo).map((l) => ` ${l}`)
41487
+ ...buildPlanForBatchQuery(stmt.expr.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`)
40020
41488
  ];
40021
41489
  }
40022
41490
  return [
@@ -40032,7 +41500,7 @@ function buildBatchStatementPlan(stmt, info) {
40032
41500
  }
40033
41501
  if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
40034
41502
  if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
40035
- if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
41503
+ if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans);
40036
41504
  if (stmt.type === "ASSERT") {
40037
41505
  const lines = [
40038
41506
  `ASSERT ${stmt.text}`,
@@ -40044,11 +41512,11 @@ function buildBatchStatementPlan(stmt, info) {
40044
41512
  subqueries.forEach((sq, i) => {
40045
41513
  lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
40046
41514
  const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
40047
- lines.push(...buildPlanForBatchQuery(sq.query, subInfo).map((l) => ` ${l}`));
41515
+ lines.push(...buildPlanForBatchQuery(sq.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`));
40048
41516
  });
40049
41517
  return lines;
40050
41518
  }
40051
- return buildPlanForBatchQuery(stmt, info);
41519
+ return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
40052
41520
  }
40053
41521
  function hasTempTableRef(node) {
40054
41522
  if (Array.isArray(node)) return node.some(hasTempTableRef);
@@ -40060,9 +41528,9 @@ function hasTempTableRef(node) {
40060
41528
  }
40061
41529
  return false;
40062
41530
  }
40063
- function buildPlanForBatchQuery(query, info) {
41531
+ function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
40064
41532
  if (info.tempTablesReferenced.length === 0) {
40065
- return buildExplainPlan(query);
41533
+ return buildExplainPlan(query, void 0, capabilities, orderPlans);
40066
41534
  }
40067
41535
  const lines = [];
40068
41536
  if (query.type === "INSERT_SELECT") {
@@ -40087,8 +41555,15 @@ function buildPlanForBatchQuery(query, info) {
40087
41555
  lines.push(" note: \u4E00\u6642\u30C6\u30FC\u30D6\u30EB\u3078\u306E WHERE \u30D7\u30C3\u30B7\u30E5\u30C0\u30A6\u30F3\u306F\u884C\u308F\u308C\u306A\u3044");
40088
41556
  return lines;
40089
41557
  }
40090
- function executeExplain(stmt) {
40091
- const lines = buildExplainPlan(stmt.query);
41558
+ async function executeExplain(stmt, client, cacheContext, maxRecords2, cursorMaxActive2) {
41559
+ const analysis = await buildExplainWhereAnalysis(stmt.query, client, cacheContext, maxRecords2);
41560
+ const lines = [
41561
+ ...explainMetadataLines(analysis),
41562
+ ...addCursorConcurrency(
41563
+ buildExplainPlan(stmt.query, void 0, analysis.capabilities, analysis.orderPlans),
41564
+ cursorMaxActive2
41565
+ )
41566
+ ];
40092
41567
  return {
40093
41568
  type: "SELECT",
40094
41569
  columns: ["plan"],
@@ -40096,31 +41571,64 @@ function executeExplain(stmt) {
40096
41571
  rowCount: lines.length
40097
41572
  };
40098
41573
  }
40099
- function buildExplainPlan(query, label) {
40100
- if (query.type === "UNION") return buildUnionPlan(query);
40101
- if (query.type === "WITH") return buildWithPlan(query);
41574
+ function addCursorConcurrency(lines, cursorMaxActive2) {
41575
+ const result = [];
41576
+ for (const line of lines) {
41577
+ result.push(line);
41578
+ if (line.trim() === "cursor page size: 500") {
41579
+ const indent = line.match(/^\s*/)?.[0] ?? "";
41580
+ result.push(`${indent}cursor concurrency: ${cursorMaxActive2} per domain (process-local)`);
41581
+ }
41582
+ }
41583
+ return result;
41584
+ }
41585
+ function buildExplainPlan(query, label, capabilities, orderPlans) {
41586
+ if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
41587
+ if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
40102
41588
  if (query.type === "INSERT") return buildInsertPlan(query, label);
40103
- if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label);
41589
+ if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
40104
41590
  if (query.type === "UPSERT") return buildUpsertPlan(query, label);
40105
- if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label);
40106
- if (query.type === "UPDATE") return buildUpdatePlan(query, label);
41591
+ if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans);
41592
+ if (query.type === "UPDATE") return buildUpdatePlan(query, label, capabilities, orderPlans);
40107
41593
  if (query.type === "DELETE") return buildDeletePlan(query, label);
40108
41594
  if (query.type === "REORDER") return buildReorderPlan(query, label);
40109
- return buildSelectPlan(query, label);
41595
+ return buildSelectPlan(query, label, capabilities, orderPlans);
40110
41596
  }
40111
- function buildSelectPlan(stmt, label) {
40112
- const mode = resolveSelectMode(stmt);
41597
+ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
41598
+ const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
41599
+ const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
41600
+ const mode = orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN" ? "FULL_SCAN" : resolveSelectMode(stmt);
40113
41601
  const reasons = collectFullScanReasons(stmt);
41602
+ if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
41603
+ reasons.push(...whereCapability.reasons.map((reason) => reason.code));
41604
+ }
40114
41605
  const lines = [];
40115
41606
  if (label) lines.push(label);
40116
41607
  lines.push(` mode: ${mode}`);
41608
+ if (orderPlan) {
41609
+ lines.push(` order plan: ${orderPlan.kind}`);
41610
+ if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
41611
+ if (orderPlan.kind === "KORDER_NATIVE") {
41612
+ lines.push(" order semantics: kintone native (not kSQL canonical)");
41613
+ lines.push(" REST execution: single GET");
41614
+ } else if (orderPlan.kind === "KORDER_CURSOR") {
41615
+ lines.push(" order semantics: kintone native (not kSQL canonical)");
41616
+ lines.push(" fetch API: POST/GET/DELETE records/cursor.json");
41617
+ lines.push(" cursor page size: 500");
41618
+ lines.push(` scan rows: ${orderPlan.scanRows}`);
41619
+ }
41620
+ }
41621
+ if (orderPlan?.requiresCompleteInput ?? requiresCompleteInput(stmt)) {
41622
+ lines.push(" complete input: required (ORDER BY / window ORDER BY; onLimit=truncate disabled)");
41623
+ }
40117
41624
  if (mode === "FULL_SCAN" && reasons.length > 0) {
40118
41625
  lines.push(` reason: ${reasons.join(", ")}`);
40119
41626
  }
40120
41627
  if (mode === "SIMPLE") {
40121
- const params = selectToKintoneParams(stmt);
41628
+ const params = selectToKintoneParams(orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt);
40122
41629
  lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
40123
- lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
41630
+ const displayedQuery = orderPlan?.kind === "KORDER_CURSOR" ? buildKorderCursorQuery(stmt) : params.query;
41631
+ lines.push(` kintone query: ${displayedQuery || "(\u306A\u3057)"}`);
40124
41632
  lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
40125
41633
  } else {
40126
41634
  const pushdownPlan = buildKlikePushdownPlan(stmt);
@@ -40128,7 +41636,8 @@ function buildSelectPlan(stmt, label) {
40128
41636
  const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
40129
41637
  const mainPushDown = pushdownPlan.mainCondition;
40130
41638
  const mainCandidate = extractMainTypedPushdownCandidate(stmt);
40131
- const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
41639
+ const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
41640
+ const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)";
40132
41641
  lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
40133
41642
  lines.push(` kintone query: ${mainQ}`);
40134
41643
  if (mainCandidate !== null) {
@@ -40150,10 +41659,10 @@ function buildSelectPlan(stmt, label) {
40150
41659
  lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
40151
41660
  }
40152
41661
  }
40153
- lines.push(...collectSubqueryPlans(stmt));
41662
+ lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
40154
41663
  return lines;
40155
41664
  }
40156
- function buildUnionPlan(stmt) {
41665
+ function buildUnionPlan(stmt, capabilities, orderPlans) {
40157
41666
  const selects = [];
40158
41667
  const collect = (u) => {
40159
41668
  if (u.type === "SELECT") {
@@ -40167,24 +41676,25 @@ function buildUnionPlan(stmt) {
40167
41676
  const lines = [];
40168
41677
  selects.forEach((sel, i) => {
40169
41678
  if (i > 0) lines.push("");
40170
- lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`));
41679
+ lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
40171
41680
  });
40172
41681
  return lines;
40173
41682
  }
40174
- function buildWithPlan(stmt) {
41683
+ function buildWithPlan(stmt, capabilities, orderPlans) {
40175
41684
  const lines = [];
40176
41685
  for (const cte of stmt.ctes) {
40177
41686
  if (cte.query.type === "SELECT") {
40178
- lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`));
41687
+ lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
40179
41688
  lines.push("");
40180
41689
  }
40181
41690
  }
40182
41691
  if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
40183
- lines.push(...buildExplainPlan(stmt.query, "[main]"));
41692
+ lines.push(...buildExplainPlan(stmt.query, "[main]", capabilities, orderPlans));
40184
41693
  }
40185
41694
  if (canInlineSingleCte(stmt)) {
40186
41695
  lines.push("");
40187
- lines.push(...buildSelectPlan(buildInlinedQuery(stmt), "[effective: inlined CTE]"));
41696
+ const inlined = buildInlinedQuery(stmt);
41697
+ lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
40188
41698
  }
40189
41699
  return lines;
40190
41700
  }
@@ -40212,7 +41722,7 @@ function collectFullScanReasons(stmt) {
40212
41722
  r.push("ORDER BY \u306B\u5F0F");
40213
41723
  return r;
40214
41724
  }
40215
- function collectSubqueryPlans(stmt) {
41725
+ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
40216
41726
  const lines = [];
40217
41727
  let idx = 1;
40218
41728
  const visitWhere = (w) => {
@@ -40221,16 +41731,16 @@ function collectSubqueryPlans(stmt) {
40221
41731
  case "BINARY":
40222
41732
  if (w.right.type === "SCALAR_SUBQUERY") {
40223
41733
  lines.push("");
40224
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`));
41734
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40225
41735
  }
40226
41736
  if (w.right.type === "SUBQUERY_IN_LIST") {
40227
41737
  lines.push("");
40228
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`));
41738
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40229
41739
  }
40230
41740
  break;
40231
41741
  case "EXISTS":
40232
41742
  lines.push("");
40233
- lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`));
41743
+ lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40234
41744
  break;
40235
41745
  case "LOGICAL":
40236
41746
  visitWhere(w.left);
@@ -40248,7 +41758,7 @@ function collectSubqueryPlans(stmt) {
40248
41758
  for (const col of stmt.columns) {
40249
41759
  if (col.type === "SCALAR_SUBQUERY_COL") {
40250
41760
  lines.push("");
40251
- lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`));
41761
+ lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40252
41762
  }
40253
41763
  }
40254
41764
  if (stmt.having) visitWhere(stmt.having);
@@ -40266,7 +41776,7 @@ function buildInsertPlan(stmt, label) {
40266
41776
  lines.push(` fields: ${stmt.fields.join(", ")}`);
40267
41777
  return lines;
40268
41778
  }
40269
- function buildInsertSelectPlan(stmt, label) {
41779
+ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
40270
41780
  const lines = [];
40271
41781
  if (label) lines.push(label);
40272
41782
  lines.push(` [INSERT SELECT]`);
@@ -40274,10 +41784,10 @@ function buildInsertSelectPlan(stmt, label) {
40274
41784
  lines.push(` fields: ${stmt.fields.join(", ")}`);
40275
41785
  lines.push(` api: POST /k/v1/records.json\uFF08\u4EF6\u6570\u306F SELECT \u7D50\u679C\u306B\u4F9D\u5B58\u3001100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`);
40276
41786
  lines.push("");
40277
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
41787
+ lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
40278
41788
  return lines;
40279
41789
  }
40280
- function buildUpdatePlan(stmt, label) {
41790
+ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
40281
41791
  const isArith = hasArithAssignment(stmt);
40282
41792
  const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
40283
41793
  const lines = [];
@@ -40311,7 +41821,7 @@ function buildUpdatePlan(stmt, label) {
40311
41821
  for (const a of stmt.assignments) {
40312
41822
  if (a.value.type === "SCALAR_SUBQUERY") {
40313
41823
  lines.push("");
40314
- lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`));
41824
+ lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans));
40315
41825
  }
40316
41826
  }
40317
41827
  return lines;
@@ -40338,7 +41848,7 @@ function buildUpsertPlan(stmt, label) {
40338
41848
  ` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json \xD7 ${batchCount}`
40339
41849
  ];
40340
41850
  }
40341
- function buildUpsertSelectPlan(stmt, label) {
41851
+ function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
40342
41852
  const lines = [
40343
41853
  ...label ? [label] : [],
40344
41854
  ` [UPSERT SELECT]`,
@@ -40348,7 +41858,7 @@ function buildUpsertSelectPlan(stmt, label) {
40348
41858
  ` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json\uFF08100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`,
40349
41859
  ``
40350
41860
  ];
40351
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
41861
+ lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
40352
41862
  return lines;
40353
41863
  }
40354
41864
  function buildReorderPlan(stmt, label) {
@@ -40661,6 +42171,12 @@ function validateKsqlConfig(config2) {
40661
42171
  }
40662
42172
  const logicalApps = normalizeLogicalApps(profileName, profile2.logicalApps);
40663
42173
  if (logicalApps !== void 0) profile2.logicalApps = logicalApps;
42174
+ if (profile2.query?.cursorMaxActive !== void 0) {
42175
+ const value = profile2.query.cursorMaxActive;
42176
+ if (!Number.isSafeInteger(value) || value < 1 || value > 5) {
42177
+ throw argumentError(`query.cursorMaxActive for profile "${profileName}" must be an integer from 1 to 5.`);
42178
+ }
42179
+ }
40664
42180
  }
40665
42181
  return config2;
40666
42182
  }
@@ -40823,6 +42339,10 @@ var RequestGate = class {
40823
42339
  async runMutation(fn) {
40824
42340
  return this.withSlot(fn);
40825
42341
  }
42342
+ /** Cursor Create/Get/Delete: セマフォのみ。GETでも位置を進めるため再試行しない。 */
42343
+ async runCursorStep(fn) {
42344
+ return this.withSlot(fn);
42345
+ }
40826
42346
  async withSlot(fn) {
40827
42347
  await this.acquire();
40828
42348
  try {
@@ -40854,6 +42374,14 @@ var RequestGate = class {
40854
42374
  function withRequestGate(client, gate) {
40855
42375
  return {
40856
42376
  getRecords: (params) => gate.runReadOnly(() => client.getRecords(params)),
42377
+ openCursor: async (params) => {
42378
+ const handle = await gate.runCursorStep(() => client.openCursor(params));
42379
+ return {
42380
+ totalCount: handle.totalCount,
42381
+ nextPage: () => gate.runCursorStep(() => handle.nextPage()),
42382
+ close: () => gate.runCursorStep(() => handle.close())
42383
+ };
42384
+ },
40857
42385
  getApps: () => gate.runReadOnly(() => client.getApps()),
40858
42386
  getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
40859
42387
  getProcessStatuses: (appId) => gate.runReadOnly(() => client.getProcessStatuses(appId)),
@@ -40883,12 +42411,14 @@ function flattenFormFieldProperties(properties) {
40883
42411
  function flattenFields(properties, lookupCopyFields, inSubtable = false) {
40884
42412
  const out = [];
40885
42413
  for (const field of Object.values(properties)) {
40886
- out.push({
42414
+ const optionOrder = toOptionOrderMap(field.options);
42415
+ const sortKind = detectSortKind(field.type, field.format);
42416
+ const info = {
40887
42417
  code: field.code,
40888
42418
  label: field.label,
40889
42419
  fieldType: field.type,
40890
- optionOrder: toOptionOrderMap(field.options),
40891
- sortKind: detectSortKind(field.type, field.format),
42420
+ optionOrder,
42421
+ sortKind,
40892
42422
  required: field.required,
40893
42423
  minValue: normalizeConstraintValue(field.minValue),
40894
42424
  maxValue: normalizeConstraintValue(field.maxValue),
@@ -40897,7 +42427,9 @@ function flattenFields(properties, lookupCopyFields, inSubtable = false) {
40897
42427
  defaultValue: field.defaultValue,
40898
42428
  inSubtable,
40899
42429
  writable: !lookupCopyFields.has(field.code) && !NON_WRITABLE_FIELD_TYPES2.has(field.type)
40900
- });
42430
+ };
42431
+ info.semantics = resolveFieldSemantics(info);
42432
+ out.push(info);
40901
42433
  if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true));
40902
42434
  }
40903
42435
  return out;
@@ -40952,7 +42484,230 @@ function detectSortKind(fieldType, calcFormat) {
40952
42484
  return void 0;
40953
42485
  }
40954
42486
 
42487
+ // src/core/processStatus.ts
42488
+ function normalizeProcessStatusStates(states) {
42489
+ if (states === null) return null;
42490
+ return Object.values(states).map((state) => {
42491
+ const index = Number(state.index);
42492
+ if (!Number.isSafeInteger(index) || index < 0) {
42493
+ throw new Error(`ArgumentError: invalid process status index: ${String(state.index)}`);
42494
+ }
42495
+ return { name: state.name, index };
42496
+ });
42497
+ }
42498
+
42499
+ // src/api/kintoneCursor.ts
42500
+ function isAlreadyReleasedCursorError(error51) {
42501
+ const shaped = error51;
42502
+ return shaped?.status === 404 && shaped.code === "GAIA_CN01";
42503
+ }
42504
+ async function deleteCursorWithConfirmation(deleteCursor, sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)), isAlreadyReleased = isAlreadyReleasedCursorError) {
42505
+ try {
42506
+ await deleteCursor();
42507
+ return;
42508
+ } catch (firstError) {
42509
+ if (isAlreadyReleased(firstError)) return;
42510
+ }
42511
+ await sleep(250);
42512
+ try {
42513
+ await deleteCursor();
42514
+ } catch (confirmationError) {
42515
+ if (isAlreadyReleased(confirmationError)) return;
42516
+ throw confirmationError;
42517
+ }
42518
+ }
42519
+ async function withTimeout(promise2, timeoutMs) {
42520
+ let timer;
42521
+ const timeout2 = new Promise((_resolve, reject) => {
42522
+ timer = setTimeout(() => reject(new Error(`CursorCleanupTimeoutError: cleanup exceeded ${timeoutMs}ms.`)), timeoutMs);
42523
+ timer.unref?.();
42524
+ });
42525
+ try {
42526
+ return await Promise.race([promise2, timeout2]);
42527
+ } finally {
42528
+ if (timer) clearTimeout(timer);
42529
+ }
42530
+ }
42531
+ function createKintoneCursorHandle(totalCount, operations) {
42532
+ let released = false;
42533
+ let closing = false;
42534
+ let pageTail = Promise.resolve();
42535
+ let closePromise = null;
42536
+ const nextPage = () => {
42537
+ if (closing || released) return Promise.resolve({ records: [], next: false });
42538
+ const result = pageTail.then(async () => {
42539
+ if (closing || released) return { records: [], next: false };
42540
+ const page = await operations.get();
42541
+ if (!page.next) {
42542
+ released = true;
42543
+ operations.onReleased?.();
42544
+ }
42545
+ return page;
42546
+ });
42547
+ pageTail = result.then(() => void 0, () => void 0);
42548
+ return result;
42549
+ };
42550
+ const close = () => {
42551
+ if (released) return Promise.resolve();
42552
+ if (closePromise) return closePromise;
42553
+ closing = true;
42554
+ closePromise = pageTail.then(async () => {
42555
+ if (released) return;
42556
+ try {
42557
+ await withTimeout(
42558
+ deleteCursorWithConfirmation(
42559
+ operations.delete,
42560
+ operations.sleep,
42561
+ operations.isAlreadyReleasedError
42562
+ ),
42563
+ operations.cleanupTimeoutMs ?? 5e3
42564
+ );
42565
+ released = true;
42566
+ operations.onReleased?.();
42567
+ } catch (error51) {
42568
+ operations.onReleaseUnknown?.();
42569
+ throw error51;
42570
+ }
42571
+ });
42572
+ return closePromise;
42573
+ };
42574
+ return { totalCount, nextPage, close };
42575
+ }
42576
+
42577
+ // src/api/cursorLeaseManager.ts
42578
+ var DEFAULT_MAX_ACTIVE = 2;
42579
+ var MAX_ACTIVE = 5;
42580
+ var DEFAULT_WAIT_MS = 3e4;
42581
+ var DEFAULT_QUARANTINE_MS = 10 * 6e4 + 3e4;
42582
+ var CursorLeaseManager = class {
42583
+ constructor(host, options = {}) {
42584
+ this.host = host;
42585
+ this.active = 0;
42586
+ this.peak = 0;
42587
+ this.quarantined = 0;
42588
+ this.waiters = [];
42589
+ this.createTail = Promise.resolve();
42590
+ const maxActive = options.maxActive ?? DEFAULT_MAX_ACTIVE;
42591
+ if (!Number.isSafeInteger(maxActive) || maxActive < 1 || maxActive > MAX_ACTIVE) {
42592
+ throw new Error(`ArgumentError: cursorMaxActive must be an integer from 1 to ${MAX_ACTIVE}.`);
42593
+ }
42594
+ this.maxActive = maxActive;
42595
+ this.waitTimeoutMs = options.waitTimeoutMs ?? DEFAULT_WAIT_MS;
42596
+ this.quarantineMs = options.quarantineMs ?? DEFAULT_QUARANTINE_MS;
42597
+ }
42598
+ acquire() {
42599
+ if (this.active < this.maxActive) {
42600
+ this.active += 1;
42601
+ this.peak = Math.max(this.peak, this.active);
42602
+ return Promise.resolve(this.makeLease());
42603
+ }
42604
+ return new Promise((resolve2, reject) => {
42605
+ const waiter = {};
42606
+ waiter.resolve = resolve2;
42607
+ waiter.reject = reject;
42608
+ waiter.timer = setTimeout(() => {
42609
+ const index = this.waiters.indexOf(waiter);
42610
+ if (index >= 0) this.waiters.splice(index, 1);
42611
+ reject(new CursorCapacityError(this.host, this.maxActive, this.waitTimeoutMs));
42612
+ }, this.waitTimeoutMs);
42613
+ waiter.timer.unref?.();
42614
+ this.waiters.push(waiter);
42615
+ });
42616
+ }
42617
+ /**
42618
+ * 同一hostを共有する後続surfaceの設定を反映する。
42619
+ * 縮小時は既存leaseを強制終了せず、activeが新上限を下回るまで新規取得だけを止める。
42620
+ */
42621
+ setMaxActive(maxActive) {
42622
+ this.validateMaxActive(maxActive);
42623
+ if (this.maxActive === maxActive) return;
42624
+ this.maxActive = maxActive;
42625
+ this.dispatchWaiters();
42626
+ }
42627
+ async runCreate(fn) {
42628
+ const previous = this.createTail;
42629
+ let unlock;
42630
+ this.createTail = new Promise((resolve2) => {
42631
+ unlock = resolve2;
42632
+ });
42633
+ await previous;
42634
+ try {
42635
+ return await fn();
42636
+ } finally {
42637
+ unlock();
42638
+ }
42639
+ }
42640
+ snapshot() {
42641
+ return {
42642
+ active: this.active,
42643
+ peak: this.peak,
42644
+ quarantined: this.quarantined,
42645
+ waiting: this.waiters.length,
42646
+ limit: this.maxActive
42647
+ };
42648
+ }
42649
+ makeLease() {
42650
+ let done = false;
42651
+ return {
42652
+ release: () => {
42653
+ if (done) return;
42654
+ done = true;
42655
+ this.returnPermit();
42656
+ },
42657
+ quarantine: (durationMs = this.quarantineMs) => {
42658
+ if (done) return;
42659
+ done = true;
42660
+ this.quarantined += 1;
42661
+ const timer = setTimeout(() => {
42662
+ this.quarantined -= 1;
42663
+ this.returnPermit();
42664
+ }, durationMs);
42665
+ timer.unref?.();
42666
+ }
42667
+ };
42668
+ }
42669
+ returnPermit() {
42670
+ this.active -= 1;
42671
+ this.dispatchWaiters();
42672
+ }
42673
+ dispatchWaiters() {
42674
+ while (this.active < this.maxActive) {
42675
+ const waiter = this.waiters.shift();
42676
+ if (!waiter) return;
42677
+ clearTimeout(waiter.timer);
42678
+ this.active += 1;
42679
+ this.peak = Math.max(this.peak, this.active);
42680
+ waiter.resolve(this.makeLease());
42681
+ }
42682
+ }
42683
+ validateMaxActive(maxActive) {
42684
+ if (!Number.isSafeInteger(maxActive) || maxActive < 1 || maxActive > MAX_ACTIVE) {
42685
+ throw new Error(`ArgumentError: cursorMaxActive must be an integer from 1 to ${MAX_ACTIVE}.`);
42686
+ }
42687
+ }
42688
+ };
42689
+ var managers = /* @__PURE__ */ new Map();
42690
+ function getCursorLeaseManager(host, maxActive = DEFAULT_MAX_ACTIVE) {
42691
+ const key = host.toLowerCase();
42692
+ let manager = managers.get(key);
42693
+ if (!manager) {
42694
+ manager = new CursorLeaseManager(key, { maxActive });
42695
+ managers.set(key, manager);
42696
+ } else {
42697
+ manager.setMaxActive(maxActive);
42698
+ }
42699
+ return manager;
42700
+ }
42701
+
40955
42702
  // src/cli/nodeKintoneClient.ts
42703
+ var KintoneApiError = class extends Error {
42704
+ constructor(status, code, bodyText) {
42705
+ super(`kintone API error ${status}: ${bodyText}`);
42706
+ this.status = status;
42707
+ this.code = code;
42708
+ this.name = "KintoneApiError";
42709
+ }
42710
+ };
40956
42711
  var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
40957
42712
  function createNodeKintoneClient(baseUrl, tokenResolver) {
40958
42713
  const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
@@ -41000,7 +42755,13 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
41000
42755
  if (tokenResolver.debug) {
41001
42756
  tokenResolver.log?.(`[debug] response status=${res.status} body=${bodyText}`);
41002
42757
  }
41003
- throw new Error(`kintone API error ${res.status}: ${bodyText}`);
42758
+ let code;
42759
+ try {
42760
+ const body = JSON.parse(bodyText);
42761
+ if (typeof body.code === "string") code = body.code;
42762
+ } catch {
42763
+ }
42764
+ throw new KintoneApiError(res.status, code, bodyText);
41004
42765
  }
41005
42766
  if (tokenResolver.debug) {
41006
42767
  tokenResolver.log?.(`[debug] response status=${res.status}`);
@@ -41067,6 +42828,48 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
41067
42828
  return response.searchAborted ? { ...response.body, searchAborted: true } : response.body;
41068
42829
  }
41069
42830
  },
42831
+ async openCursor(params) {
42832
+ const manager = getCursorLeaseManager(new URL(normalizedBaseUrl).host, tokenResolver.cursorMaxActive);
42833
+ const lease = await manager.acquire();
42834
+ let created;
42835
+ try {
42836
+ created = await manager.runCreate(() => requestJson(
42837
+ `${apiBasePath}/records/cursor.json`,
42838
+ {
42839
+ method: "POST",
42840
+ body: JSON.stringify({
42841
+ app: params.app,
42842
+ query: params.query,
42843
+ size: params.size,
42844
+ fields: params.fields && params.fields.length > 0 ? params.fields : void 0
42845
+ })
42846
+ },
42847
+ params.app
42848
+ ));
42849
+ } catch (error51) {
42850
+ if (error51 instanceof KintoneApiError) {
42851
+ lease.release();
42852
+ throw error51;
42853
+ }
42854
+ lease.quarantine();
42855
+ throw new CursorCreateOutcomeUnknownError(error51);
42856
+ }
42857
+ const cursorId = created.id;
42858
+ return createKintoneCursorHandle(Number(created.totalCount), {
42859
+ get: () => requestJson(
42860
+ `${apiBasePath}/records/cursor.json?id=${encodeURIComponent(cursorId)}`,
42861
+ { method: "GET" },
42862
+ params.app
42863
+ ),
42864
+ delete: () => requestJson(
42865
+ `${apiBasePath}/records/cursor.json`,
42866
+ { method: "DELETE", body: JSON.stringify({ id: cursorId }) },
42867
+ params.app
42868
+ ),
42869
+ onReleased: () => lease.release(),
42870
+ onReleaseUnknown: () => lease.quarantine()
42871
+ });
42872
+ },
41070
42873
  async postRecords(_params) {
41071
42874
  const res = await requestJson(
41072
42875
  `${apiBasePath}/records.json`,
@@ -41149,7 +42952,7 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
41149
42952
  const res = await requestJson(`${apiBasePath}/app/status.json?${qs.toString()}`, { method: "GET" }, appId);
41150
42953
  return {
41151
42954
  enable: res.enable,
41152
- states: Object.values(res.states ?? {}).map((state) => state.name)
42955
+ states: normalizeProcessStatusStates(res.states)
41153
42956
  };
41154
42957
  }
41155
42958
  };
@@ -41533,6 +43336,10 @@ async function createKsqlRuntime(serverOptions, input) {
41533
43336
  const onLimit2 = input.onLimit ?? envOnLimit("KSQL_ON_LIMIT") ?? profile2.query?.onLimit ?? "error";
41534
43337
  const timeout2 = input.timeout ?? envInt("KSQL_TIMEOUT") ?? profile2.query?.timeout ?? 3e4;
41535
43338
  const tempTableMaxRows2 = input.tempTableMaxRows ?? envInt("KSQL_TEMP_TABLE_MAX_ROWS") ?? profile2.query?.tempTableMaxRows;
43339
+ const cursorMaxActive2 = input.cursorMaxActive ?? envInt("KSQL_CURSOR_MAX_ACTIVE") ?? profile2.query?.cursorMaxActive ?? 2;
43340
+ if (!Number.isSafeInteger(cursorMaxActive2) || cursorMaxActive2 < 1 || cursorMaxActive2 > 5) {
43341
+ throw new Error("ArgumentError: cursorMaxActive must be an integer from 1 to 5.");
43342
+ }
41536
43343
  const appIds = extractAppIds(sql);
41537
43344
  const defaultApp = envInt("KSQL_APP") ?? profile2.app ?? null;
41538
43345
  if (appIds.length === 0 && defaultApp !== null) appIds.push(defaultApp);
@@ -41570,6 +43377,7 @@ async function createKsqlRuntime(serverOptions, input) {
41570
43377
  }
41571
43378
  profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
41572
43379
  guestSpaceId,
43380
+ cursorMaxActive: cursorMaxActive2,
41573
43381
  timeoutMs: timeout2,
41574
43382
  debug: input.debug,
41575
43383
  debugHeaders: input.debugHeaders,
@@ -41601,6 +43409,7 @@ async function createKsqlRuntime(serverOptions, input) {
41601
43409
  }
41602
43410
  profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
41603
43411
  guestSpaceId,
43412
+ cursorMaxActive: cursorMaxActive2,
41604
43413
  timeoutMs: timeout2,
41605
43414
  debug: input.debug,
41606
43415
  debugHeaders: input.debugHeaders,
@@ -41634,6 +43443,12 @@ async function createKsqlRuntime(serverOptions, input) {
41634
43443
  if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
41635
43444
  return routed.getRecords({ ...params, app: binding.appId });
41636
43445
  },
43446
+ openCursor: (params) => {
43447
+ const binding = resolveRuntimeBinding(runtimeContext.sqlContext, params.app);
43448
+ const routed = runtimeContext.clientsByProfile.get(binding.profile);
43449
+ if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
43450
+ return routed.openCursor({ ...params, app: binding.appId });
43451
+ },
41637
43452
  postRecords: (params) => {
41638
43453
  const binding = resolveRuntimeBinding(runtimeContext.sqlContext, params.app);
41639
43454
  const routed = runtimeContext.clientsByProfile.get(binding.profile);
@@ -41684,6 +43499,7 @@ async function createKsqlRuntime(serverOptions, input) {
41684
43499
  fetchParallel: fetchParallel2,
41685
43500
  onLimit: onLimit2,
41686
43501
  timeout: timeout2,
43502
+ cursorMaxActive: cursorMaxActive2,
41687
43503
  tempTableMaxRows: tempTableMaxRows2
41688
43504
  };
41689
43505
  }
@@ -41884,6 +43700,7 @@ function noOpClient() {
41884
43700
  };
41885
43701
  return {
41886
43702
  getRecords: fail,
43703
+ openCursor: fail,
41887
43704
  postRecords: fail,
41888
43705
  putRecords: fail,
41889
43706
  deleteRecords: fail,
@@ -42156,8 +43973,26 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42156
43973
  } catch (err) {
42157
43974
  throw restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
42158
43975
  }
43976
+ const needsAppMetadata = normalized.appBindingByMappedApp.size > 0 && statements.some(explainNeedsAppMetadata);
43977
+ const runtime = needsAppMetadata ? await createRuntime(serverOptions, {
43978
+ sql: input.sql,
43979
+ sqlContext: normalized.sqlContext,
43980
+ profile: input.profile,
43981
+ maxRecords: input.maxRecords,
43982
+ cursorMaxActive: input.cursorMaxActive
43983
+ }) : null;
43984
+ const explainClient = runtime?.client ?? noOpClient();
43985
+ const explainCacheContext = runtime?.cacheContext ?? normalized.cacheContext;
43986
+ const explainSourceSql = runtime?.sql ?? normalized.normalizedSql;
42159
43987
  if (statements.length > 1) {
42160
- const plans = buildBatchExplainPlans(normalized.normalizedSql);
43988
+ const plans = await buildBatchExplainPlans(
43989
+ explainSourceSql,
43990
+ explainClient,
43991
+ void 0,
43992
+ explainCacheContext,
43993
+ runtime?.maxRecords ?? input.maxRecords,
43994
+ runtime?.cursorMaxActive ?? input.cursorMaxActive ?? 2
43995
+ );
42161
43996
  return {
42162
43997
  ok: true,
42163
43998
  batch: true,
@@ -42166,8 +44001,10 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42166
44001
  appBindings
42167
44002
  };
42168
44003
  }
42169
- const result = await executeSql(explainSql(normalized.normalizedSql), noOpClient(), {
42170
- cacheContext: normalized.cacheContext
44004
+ const result = await executeSql(explainSql(explainSourceSql), explainClient, {
44005
+ cacheContext: explainCacheContext,
44006
+ maxRecords: runtime?.maxRecords ?? input.maxRecords,
44007
+ cursorMaxActive: runtime?.cursorMaxActive ?? input.cursorMaxActive ?? 2
42171
44008
  });
42172
44009
  if (result.type !== "SELECT") {
42173
44010
  throw new Error(`ArgumentError: EXPLAIN returned unexpected result type ${result.type}.`);
@@ -42192,9 +44029,10 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42192
44029
  profile: input.profile,
42193
44030
  maxRecords: input.maxRecords,
42194
44031
  fetchParallel: input.fetchParallel,
42195
- onLimit: validation.requiresCompleteInput ? "error" : input.onLimit,
44032
+ onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
42196
44033
  timeout: input.timeout,
42197
- tempTableMaxRows: input.tempTableMaxRows
44034
+ tempTableMaxRows: input.tempTableMaxRows,
44035
+ cursorMaxActive: input.cursorMaxActive
42198
44036
  });
42199
44037
  const batchResult = await executeBatchSql(runtime2.sql, runtime2.client, {
42200
44038
  maxRecords: runtime2.maxRecords,
@@ -42209,6 +44047,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42209
44047
  // runtime.timeout は env / profile / 既定 30000ms を解決済みの値で、
42210
44048
  // HTTP クライアント側の per-request タイムアウトと同値になる
42211
44049
  timeoutMs: runtime2.timeout,
44050
+ cursorMaxActive: runtime2.cursorMaxActive ?? input.cursorMaxActive ?? 2,
42212
44051
  variables: input.variables
42213
44052
  });
42214
44053
  return { ...buildBatchEnvelope(batchResult, { maxTotalRecords: input.maxTotalRecords }) };
@@ -42237,14 +44076,16 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42237
44076
  profile: input.profile,
42238
44077
  maxRecords: input.maxRecords,
42239
44078
  fetchParallel: input.fetchParallel,
42240
- onLimit: validation.requiresCompleteInput ? "error" : input.onLimit,
42241
- timeout: input.timeout
44079
+ onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
44080
+ timeout: input.timeout,
44081
+ cursorMaxActive: input.cursorMaxActive
42242
44082
  });
42243
44083
  const result = await executeSql(runtime.sql, runtime.client, {
42244
44084
  maxRecords: runtime.maxRecords,
42245
44085
  fetchParallel: runtime.fetchParallel,
42246
44086
  onLimitReached: runtime.onLimit,
42247
- cacheContext: runtime.cacheContext
44087
+ cacheContext: runtime.cacheContext,
44088
+ cursorMaxActive: runtime.cursorMaxActive ?? input.cursorMaxActive ?? 2
42248
44089
  });
42249
44090
  if (result.type === "ASSERT") return toAssertPayload(result);
42250
44091
  if (result.type === "VALIDATION") return toDmlValidationPayload(result);
@@ -42288,7 +44129,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42288
44129
  fetchParallel: input.fetchParallel,
42289
44130
  onLimit: DEFAULT_ON_LIMIT,
42290
44131
  timeout: input.timeout,
42291
- tempTableMaxRows: input.tempTableMaxRows
44132
+ tempTableMaxRows: input.tempTableMaxRows,
44133
+ cursorMaxActive: input.cursorMaxActive
42292
44134
  });
42293
44135
  let totalAffected = staticInsertTotal;
42294
44136
  const batchResult = await executeBatchSql(runtime.sql, runtime.client, {
@@ -42300,6 +44142,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42300
44142
  tempTableMaxRows: runtime.tempTableMaxRows,
42301
44143
  // 合計タイムアウト(解決済みの runtime.timeout。per-request と同値)
42302
44144
  timeoutMs: runtime.timeout,
44145
+ cursorMaxActive: runtime.cursorMaxActive ?? input.cursorMaxActive ?? 2,
42303
44146
  variables: input.variables,
42304
44147
  confirm: async (count, operation) => {
42305
44148
  if (count > dmlMaxRows) {
@@ -42355,7 +44198,8 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42355
44198
  maxRecords: resolveMutateRuntimeMaxRecords(validation.statements, dmlMaxRows),
42356
44199
  fetchParallel: input.fetchParallel,
42357
44200
  onLimit: DEFAULT_ON_LIMIT,
42358
- timeout: input.timeout
44201
+ timeout: input.timeout,
44202
+ cursorMaxActive: input.cursorMaxActive
42359
44203
  });
42360
44204
  let result;
42361
44205
  try {
@@ -42364,6 +44208,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42364
44208
  fetchParallel: runtime.fetchParallel,
42365
44209
  onLimitReached: runtime.onLimit,
42366
44210
  cacheContext: runtime.cacheContext,
44211
+ cursorMaxActive: runtime.cursorMaxActive ?? input.cursorMaxActive ?? 2,
42367
44212
  confirm: async (count, operation) => {
42368
44213
  if (count > dmlMaxRows) {
42369
44214
  throw new Error(`ArgumentError: ${operation} affected rows (${count}) exceed dmlMaxRows (${dmlMaxRows}).`);
@@ -42533,9 +44378,10 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42533
44378
  var profile = external_exports.string().min(1).describe("kintone connection profile name from ksql.config.json (default: the server's default profile).").optional();
42534
44379
  var maxRecords = external_exports.number().int().positive().describe("Maximum records fetched per SELECT (default 500).").optional();
42535
44380
  var fetchParallel = external_exports.number().int().min(1).max(10).describe("Number of parallel kintone record-fetch requests (1-10).").optional();
42536
- var onLimit = external_exports.enum(["error", "truncate"]).describe("Behavior when maxRecords is exceeded: 'error' rejects, 'truncate' returns the first maxRecords rows (default 'error'). VALIDATE ONLY always requires complete input and therefore overrides 'truncate' to 'error'.").optional();
44381
+ var onLimit = external_exports.enum(["error", "truncate"]).describe("Behavior when maxRecords is exceeded: 'error' rejects, 'truncate' returns the first maxRecords rows (default 'error'). Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. VALIDATE ONLY always overrides 'truncate' to 'error'.").optional();
42537
44382
  var tempTableMaxRows = external_exports.number().int().positive().describe("Per-temp-table cap on materialized rows for CREATE TEMP TABLE ... AS SELECT (default 10000). Overflow always errors \u2014 'truncate' never applies to temp tables, so downstream statements never see silently truncated data. Raising this increases memory use (up to 16 temp tables per batch); prefer narrowing the SELECT with WHERE.").optional();
42538
44383
  var timeout = external_exports.number().int().positive().describe("Request timeout in milliseconds. For multi-statement batches this also acts as the total batch deadline.").optional();
44384
+ var cursorMaxActive = external_exports.number().int().min(1).max(5).describe("Maximum active Cursor API handles per kintone host in this process (1-5, default 2). Later calls update the host limit; lowering it keeps existing cursors and delays new ones until active usage falls below the new limit. Create/Get are never automatically retried; capacity waits up to 30 seconds.").optional();
42539
44385
  var savedQueryName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/).describe("Saved query name (alphanumeric, '_' and '-', up to 64 chars).");
42540
44386
  var savedQueryTags = external_exports.array(external_exports.string().min(1)).describe("Tags for organizing saved queries.").optional();
42541
44387
  var validateInputSchema = external_exports.object({
@@ -42544,7 +44390,9 @@ var validateInputSchema = external_exports.object({
42544
44390
  });
42545
44391
  var explainInputSchema = external_exports.object({
42546
44392
  sql: external_exports.string().min(1).describe("kSQL text to explain. May contain multiple ;-separated statements (batch) and temp tables (#name)."),
42547
- profile
44393
+ profile,
44394
+ maxRecords,
44395
+ cursorMaxActive
42548
44396
  });
42549
44397
  var queryInputSchema = external_exports.object({
42550
44398
  sql: external_exports.string().min(1).describe("Read-only kSQL text. May contain multiple ;-separated statements (batch) with temp tables, e.g. CREATE TEMP TABLE #t AS SELECT ...; SELECT ... FROM #t;"),
@@ -42554,6 +44402,7 @@ var queryInputSchema = external_exports.object({
42554
44402
  onLimit,
42555
44403
  tempTableMaxRows,
42556
44404
  timeout,
44405
+ cursorMaxActive,
42557
44406
  continueOnError: external_exports.boolean().describe("Batch (multi-statement) only: keep executing subsequent statements after a runtime error (default false = fail-fast).").optional(),
42558
44407
  maxTotalRecords: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total rows returned across all result sets (default: unlimited).").optional(),
42559
44408
  variables: external_exports.record(external_exports.string(), external_exports.string()).describe("Batch only: string values for variables declared with DECLARE. Keys omit @ and are case-insensitive.").optional()
@@ -42567,6 +44416,7 @@ var mutateInputSchema = external_exports.object({
42567
44416
  fetchParallel,
42568
44417
  tempTableMaxRows,
42569
44418
  timeout,
44419
+ cursorMaxActive,
42570
44420
  dmlTotalMaxRows: external_exports.number().int().positive().describe("Batch (multi-statement) only: cap on total affected rows across the whole batch (default: per-statement dmlMaxRows only). DML batches always run fail-fast.").optional(),
42571
44421
  variables: external_exports.record(external_exports.string(), external_exports.string()).describe("Batch only: string values for variables declared with DECLARE. Keys omit @ and are case-insensitive.").optional()
42572
44422
  });
@@ -42657,7 +44507,7 @@ Options:
42657
44507
  -h, --help Show help
42658
44508
  `);
42659
44509
  }
42660
- var SERVER_VERSION = true ? "2.17.0" : "0.0.0-dev";
44510
+ var SERVER_VERSION = true ? "3.1.0" : "0.0.0-dev";
42661
44511
  function createServer(args) {
42662
44512
  const server = new McpServer({
42663
44513
  name: "ksql-mcp",
@@ -42674,12 +44524,12 @@ function createServer(args) {
42674
44524
  }, tools.validateTool);
42675
44525
  server.registerTool("ksql_explain", {
42676
44526
  title: "Explain kSQL",
42677
- description: "Return the kSQL execution plan without calling kintone APIs.",
44527
+ description: "Return the schema-aware kSQL execution plan. Reads form metadata and, when needed, process status metadata; never reads or writes records.",
42678
44528
  inputSchema: explainInputShape
42679
44529
  }, tools.explainTool);
42680
44530
  server.registerTool("ksql_query", {
42681
44531
  title: "Run read-only kSQL",
42682
- description: "Execute read-only kSQL: SELECT, WITH, UNION, EXPLAIN, SHOW APPS, DESCRIBE, ASSERT, and INSERT/UPSERT/UPDATE ... VALIDATE ONLY. ASSERT failure always stops the batch. VALIDATE ONLY performs local Tier-0 validation with zero write API calls; it always requires complete input, so onLimit=truncate is ignored and treated as error. Supports multi-statement batches with temp tables, including VALIDATE ONLY INTO #err for later SELECT. Mutating DML is rejected.",
44532
+ description: "Execute read-only kSQL: SELECT, WITH, UNION, EXPLAIN, SHOW APPS, DESCRIBE, ASSERT, and INSERT/UPSERT/UPDATE ... VALIDATE ONLY. ASSERT failure always stops the batch. Local ORDER BY plans require complete input and fail instead of returning a truncated top-N; REST top-N and KORDER_NATIVE do not fetch a partial candidate set. VALIDATE ONLY always treats onLimit=truncate as error. VALIDATE ONLY performs local Tier-0 validation with zero write API calls. Supports multi-statement batches with temp tables, including VALIDATE ONLY INTO #err for later SELECT. Mutating DML is rejected.",
42683
44533
  inputSchema: queryInputShape
42684
44534
  }, tools.queryTool);
42685
44535
  server.registerTool("ksql_mutate", {