@rex0220/kintone-sql-tools 2.16.0 → 3.0.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 */],
@@ -31035,6 +31036,11 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31035
31036
  ["COALESCE", "COALESCE" /* COALESCE */],
31036
31037
  ["NULLIF", "NULLIF" /* NULLIF */],
31037
31038
  ["ISNULL", "ISNULL" /* ISNULL */],
31039
+ ["INSTR", "INSTR" /* INSTR */],
31040
+ ["GREATEST", "GREATEST" /* GREATEST */],
31041
+ ["LEAST", "LEAST" /* LEAST */],
31042
+ ["LPAD", "LPAD" /* LPAD */],
31043
+ ["RPAD", "RPAD" /* RPAD */],
31038
31044
  ["CAST", "CAST" /* CAST */],
31039
31045
  ["CONVERT", "CONVERT" /* CONVERT */],
31040
31046
  ["FORMAT", "FORMAT" /* FORMAT */],
@@ -31042,6 +31048,8 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31042
31048
  ["FLOOR", "FLOOR" /* FLOOR */],
31043
31049
  ["CEIL", "CEIL" /* CEIL */],
31044
31050
  ["CEILING", "CEILING" /* CEILING */],
31051
+ ["TRUNCATE", "TRUNCATE" /* TRUNCATE */],
31052
+ ["TRUNC", "TRUNC" /* TRUNC */],
31045
31053
  ["ABS", "ABS" /* ABS */],
31046
31054
  ["MOD", "MOD" /* MOD */],
31047
31055
  ["POWER", "POWER" /* POWER */],
@@ -31053,6 +31061,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31053
31061
  ["DATE_FORMAT", "DATE_FORMAT" /* DATE_FORMAT */],
31054
31062
  ["DATEDIFF", "DATEDIFF" /* DATEDIFF */],
31055
31063
  ["DATE_ADD", "DATE_ADD" /* DATE_ADD */],
31064
+ ["LAST_DAY", "LAST_DAY" /* LAST_DAY */],
31056
31065
  ["IF", "IF" /* IF */]
31057
31066
  ]);
31058
31067
 
@@ -31398,6 +31407,13 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31398
31407
  "COALESCE" /* COALESCE */,
31399
31408
  "NULLIF" /* NULLIF */,
31400
31409
  "ISNULL" /* ISNULL */,
31410
+ "LEFT" /* LEFT */,
31411
+ "RIGHT" /* RIGHT */,
31412
+ "INSTR" /* INSTR */,
31413
+ "GREATEST" /* GREATEST */,
31414
+ "LEAST" /* LEAST */,
31415
+ "LPAD" /* LPAD */,
31416
+ "RPAD" /* RPAD */,
31401
31417
  "CAST" /* CAST */,
31402
31418
  "CONVERT" /* CONVERT */,
31403
31419
  "FORMAT" /* FORMAT */,
@@ -31405,6 +31421,8 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31405
31421
  "FLOOR" /* FLOOR */,
31406
31422
  "CEIL" /* CEIL */,
31407
31423
  "CEILING" /* CEILING */,
31424
+ "TRUNCATE" /* TRUNCATE */,
31425
+ "TRUNC" /* TRUNC */,
31408
31426
  "ABS" /* ABS */,
31409
31427
  "MOD" /* MOD */,
31410
31428
  "POWER" /* POWER */,
@@ -31416,6 +31434,7 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31416
31434
  "DATE_FORMAT" /* DATE_FORMAT */,
31417
31435
  "DATEDIFF" /* DATEDIFF */,
31418
31436
  "DATE_ADD" /* DATE_ADD */,
31437
+ "LAST_DAY" /* LAST_DAY */,
31419
31438
  "IF" /* IF */
31420
31439
  ]);
31421
31440
  function needsSpaceBetween(prev, cur) {
@@ -31497,7 +31516,7 @@ var Parser = class {
31497
31516
  case "WITH" /* WITH */:
31498
31517
  return this.parseWith();
31499
31518
  case "SELECT" /* SELECT */:
31500
- return this.tryParseUnionChain(this.parseSelect());
31519
+ return this.tryParseUnionChain(this.parseSelect(true));
31501
31520
  case "INSERT" /* INSERT */:
31502
31521
  return this.parseInsert();
31503
31522
  case "UPDATE" /* UPDATE */:
@@ -31687,7 +31706,7 @@ var Parser = class {
31687
31706
  }
31688
31707
  query = w;
31689
31708
  } else if (tok.kind === "SELECT" /* SELECT */) {
31690
- const sel = this.parseSelect();
31709
+ const sel = this.parseSelect(true);
31691
31710
  const chained = this.tryParseUnionChain(sel);
31692
31711
  query = chained;
31693
31712
  } else if (tok.kind === "INSERT" /* INSERT */) {
@@ -31880,7 +31899,7 @@ var Parser = class {
31880
31899
  // ----------------------------------------------------------
31881
31900
  // SELECT
31882
31901
  // ----------------------------------------------------------
31883
- parseSelect() {
31902
+ parseSelect(allowKorder = false) {
31884
31903
  this.expect("SELECT" /* SELECT */);
31885
31904
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
31886
31905
  const columns = this.parseSelectColumns();
@@ -31897,7 +31916,19 @@ var Parser = class {
31897
31916
  having = this.parseWhereExpr();
31898
31917
  }
31899
31918
  }
31900
- 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
+ }
31901
31932
  const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
31902
31933
  const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
31903
31934
  const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
@@ -31914,6 +31945,7 @@ var Parser = class {
31914
31945
  where,
31915
31946
  groupBy,
31916
31947
  having,
31948
+ orderMode,
31917
31949
  orderBy,
31918
31950
  limit,
31919
31951
  offset
@@ -31951,6 +31983,9 @@ var Parser = class {
31951
31983
  // ----------------------------------------------------------
31952
31984
  tryParseUnionChain(left) {
31953
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
+ }
31954
31989
  this.advance();
31955
31990
  const all = this.consume("ALL" /* ALL */);
31956
31991
  const right = this.parseSelect();
@@ -32288,6 +32323,10 @@ var Parser = class {
32288
32323
  // arg: 文字列リテラル / 算術式(フィールド参照・数値含む)/ ネスト文字列関数
32289
32324
  // ──────────────────────────────────────────────────
32290
32325
  tryStringFuncName() {
32326
+ if (this.peekAt(1).kind === "(" /* LPAREN */) {
32327
+ if (this.peek().kind === "LEFT" /* LEFT */) return "LEFT";
32328
+ if (this.peek().kind === "RIGHT" /* RIGHT */) return "RIGHT";
32329
+ }
32291
32330
  const map2 = {
32292
32331
  ["UPPER" /* UPPER */]: "UPPER",
32293
32332
  ["LOWER" /* LOWER */]: "LOWER",
@@ -32302,6 +32341,11 @@ var Parser = class {
32302
32341
  ["COALESCE" /* COALESCE */]: "COALESCE",
32303
32342
  ["NULLIF" /* NULLIF */]: "NULLIF",
32304
32343
  ["ISNULL" /* ISNULL */]: "ISNULL",
32344
+ ["INSTR" /* INSTR */]: "INSTR",
32345
+ ["GREATEST" /* GREATEST */]: "GREATEST",
32346
+ ["LEAST" /* LEAST */]: "LEAST",
32347
+ ["LPAD" /* LPAD */]: "LPAD",
32348
+ ["RPAD" /* RPAD */]: "RPAD",
32305
32349
  ["CAST" /* CAST */]: "CAST",
32306
32350
  ["CONVERT" /* CONVERT */]: "CAST",
32307
32351
  // CONVERT → CAST に正規化
@@ -32311,12 +32355,16 @@ var Parser = class {
32311
32355
  ["CEIL" /* CEIL */]: "CEIL",
32312
32356
  ["CEILING" /* CEILING */]: "CEIL",
32313
32357
  // CEILING → CEIL に正規化
32358
+ ["TRUNCATE" /* TRUNCATE */]: "TRUNCATE",
32359
+ ["TRUNC" /* TRUNC */]: "TRUNCATE",
32360
+ // TRUNC → TRUNCATE に正規化
32314
32361
  ["YEAR" /* YEAR */]: "YEAR",
32315
32362
  ["MONTH" /* MONTH */]: "MONTH",
32316
32363
  ["DAY" /* DAY */]: "DAY",
32317
32364
  ["DATE_FORMAT" /* DATE_FORMAT */]: "DATE_FORMAT",
32318
32365
  ["DATEDIFF" /* DATEDIFF */]: "DATEDIFF",
32319
32366
  ["DATE_ADD" /* DATE_ADD */]: "DATE_ADD",
32367
+ ["LAST_DAY" /* LAST_DAY */]: "LAST_DAY",
32320
32368
  ["ABS" /* ABS */]: "ABS",
32321
32369
  ["MOD" /* MOD */]: "MOD",
32322
32370
  ["POWER" /* POWER */]: "POWER",
@@ -33591,7 +33639,50 @@ function isReadOnlyStatement(stmt) {
33591
33639
  return !writesKintone(stmt) && (isReadOnlyType(stmt.type) || isDmlType(stmt.type));
33592
33640
  }
33593
33641
  function requiresCompleteInput(stmt) {
33594
- 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
+ }
33595
33686
  }
33596
33687
  function hasWhereClause(stmt) {
33597
33688
  if (!stmt || typeof stmt !== "object") return false;
@@ -34394,6 +34485,7 @@ function buildInlinedQuery(stmt) {
34394
34485
  where,
34395
34486
  groupBy: [],
34396
34487
  having: null,
34488
+ orderMode: "CANONICAL",
34397
34489
  orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
34398
34490
  limit: final.limit ?? cteBody.limit,
34399
34491
  offset: final.offset ?? cteBody.offset,
@@ -34988,6 +35080,72 @@ function analyzeBatch(statements) {
34988
35080
  };
34989
35081
  }
34990
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
+
34991
35149
  // src/core/batchVariables.ts
34992
35150
  var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
34993
35151
  function normalizeBatchVariableName(name) {
@@ -35023,26 +35181,197 @@ function validateDeclaredBatchVariables(statements, input) {
35023
35181
  }
35024
35182
 
35025
35183
  // src/core/scalarCompare.ts
35026
- function compareScalarValues(op, leftStr, rightStr) {
35027
- if (op === "=") return leftStr === rightStr;
35028
- if (op === "!=" || op === "<>") return leftStr !== rightStr;
35029
- const rightNum = Number(rightStr);
35030
- if (leftStr === "" && rightStr !== "" && Number.isFinite(rightNum)) {
35031
- return op === "<" || op === "<=";
35032
- }
35033
- const leftNum = Number(leftStr);
35034
- 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);
35035
35293
  switch (op) {
35294
+ case "=":
35295
+ return cmp === 0;
35296
+ case "!=":
35297
+ case "<>":
35298
+ return cmp !== 0;
35036
35299
  case ">":
35037
- return numeric ? leftNum > rightNum : leftStr > rightStr;
35300
+ return cmp > 0;
35038
35301
  case "<":
35039
- return numeric ? leftNum < rightNum : leftStr < rightStr;
35302
+ return cmp < 0;
35040
35303
  case ">=":
35041
- return numeric ? leftNum >= rightNum : leftStr >= rightStr;
35304
+ return cmp >= 0;
35042
35305
  case "<=":
35043
- return numeric ? leftNum <= rightNum : leftStr <= rightStr;
35306
+ return cmp <= 0;
35044
35307
  }
35045
35308
  }
35309
+ function selectScalarExtreme(values, extreme) {
35310
+ if (extreme === "least" && values.includes("")) return "";
35311
+ const candidates = extreme === "greatest" ? values.filter((value) => value !== "") : [...values];
35312
+ if (candidates.length === 0) return "";
35313
+ const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
35314
+ const compare = (left, right) => {
35315
+ if (numeric) {
35316
+ const numericCmp = triCompare(Number(left), Number(right));
35317
+ if (numericCmp !== 0) return numericCmp;
35318
+ }
35319
+ return compareCodePointStrings(left, right);
35320
+ };
35321
+ return candidates.reduce((best, candidate) => {
35322
+ const cmp = compare(candidate, best);
35323
+ return extreme === "greatest" ? cmp > 0 ? candidate : best : cmp < 0 ? candidate : best;
35324
+ });
35325
+ }
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
+ }
35046
35375
 
35047
35376
  // src/engine/evalFunc.ts
35048
35377
  function evalArithExpr(expr, row) {
@@ -35091,6 +35420,36 @@ function evalStringFunc(expr, row) {
35091
35420
  const len = args[2] !== void 0 ? Number(args[2]) : void 0;
35092
35421
  return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
35093
35422
  }
35423
+ case "LEFT": {
35424
+ assertArity("LEFT", args, 2, 2);
35425
+ const str = args[0];
35426
+ const n = Math.trunc(Number(args[1]));
35427
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
35428
+ }
35429
+ case "RIGHT": {
35430
+ assertArity("RIGHT", args, 2, 2);
35431
+ const str = args[0];
35432
+ const n = Math.trunc(Number(args[1]));
35433
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
35434
+ }
35435
+ case "INSTR":
35436
+ assertArity("INSTR", args, 2, 2);
35437
+ return String(args[0].indexOf(args[1]) + 1);
35438
+ case "LPAD":
35439
+ case "RPAD": {
35440
+ assertArity(expr.func, args, 2, 3);
35441
+ const str = args[0];
35442
+ const n = Math.trunc(Number(args[1]));
35443
+ if (Number.isNaN(n) || n <= 0) return "";
35444
+ if (str.length >= n) return str.slice(0, n);
35445
+ const pad = args[2] ?? " ";
35446
+ if (pad === "") return str;
35447
+ return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
35448
+ }
35449
+ case "GREATEST":
35450
+ case "LEAST":
35451
+ assertArity(expr.func, args, 2);
35452
+ return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
35094
35453
  case "CONCAT":
35095
35454
  return args.join("");
35096
35455
  case "REPLACE": {
@@ -35111,6 +35470,9 @@ function evalStringFunc(expr, row) {
35111
35470
  return applyRoundOp("floor", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
35112
35471
  case "CEIL":
35113
35472
  return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
35473
+ case "TRUNCATE":
35474
+ assertArity("TRUNCATE", args, 1, 2);
35475
+ return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
35114
35476
  case "CAST": {
35115
35477
  const val = args[0] ?? "";
35116
35478
  const castType = args[1] ?? "TEXT";
@@ -35143,6 +35505,9 @@ function evalStringFunc(expr, row) {
35143
35505
  return applyDateDiff(args[0] ?? "", args[1] ?? "");
35144
35506
  case "DATE_ADD":
35145
35507
  return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
35508
+ case "LAST_DAY":
35509
+ assertArity("LAST_DAY", args, 1, 1);
35510
+ return applyLastDay(args[0]);
35146
35511
  case "ABS":
35147
35512
  return String(Math.abs(Number(args[0] ?? "0")));
35148
35513
  case "MOD": {
@@ -35165,6 +35530,11 @@ function evalStringFunc(expr, row) {
35165
35530
  return (/* @__PURE__ */ new Date()).toISOString();
35166
35531
  }
35167
35532
  }
35533
+ function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
35534
+ if (args.length >= min && args.length <= max) return;
35535
+ const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
35536
+ throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
35537
+ }
35168
35538
  function parseDateParts(s) {
35169
35539
  return {
35170
35540
  y: s.slice(0, 4) || "0000",
@@ -35189,6 +35559,9 @@ function applyDateDiff(date1, date22) {
35189
35559
  return String(Math.round((d1 - d2) / 864e5));
35190
35560
  }
35191
35561
  function applyDateAdd(dateStr, n, unit) {
35562
+ if (unit !== "YEAR" && unit !== "MONTH" && unit !== "DAY") {
35563
+ throw new Error("ArgumentError: DATE_ADD unit must be YEAR, MONTH, or DAY.");
35564
+ }
35192
35565
  if (!dateStr || dateStr.length < 10) return dateStr;
35193
35566
  const { y, mo, d } = parseDateParts(dateStr);
35194
35567
  const dt = new Date(Date.UTC(+y, +mo - 1, +d));
@@ -35199,7 +35572,7 @@ function applyDateAdd(dateStr, n, unit) {
35199
35572
  case "MONTH":
35200
35573
  dt.setUTCMonth(dt.getUTCMonth() + n);
35201
35574
  break;
35202
- default:
35575
+ case "DAY":
35203
35576
  dt.setUTCDate(dt.getUTCDate() + n);
35204
35577
  break;
35205
35578
  }
@@ -35208,6 +35581,15 @@ function applyDateAdd(dateStr, n, unit) {
35208
35581
  const rd = String(dt.getUTCDate()).padStart(2, "0");
35209
35582
  return `${ry}-${rmo}-${rd}`;
35210
35583
  }
35584
+ function applyLastDay(dateStr) {
35585
+ if (!dateStr || dateStr.length < 10) return dateStr;
35586
+ const { y, mo } = parseDateParts(dateStr);
35587
+ const dt = new Date(Date.UTC(+y, +mo, 0));
35588
+ const ry = String(dt.getUTCFullYear()).padStart(4, "0");
35589
+ const rmo = String(dt.getUTCMonth() + 1).padStart(2, "0");
35590
+ const rd = String(dt.getUTCDate()).padStart(2, "0");
35591
+ return `${ry}-${rmo}-${rd}`;
35592
+ }
35211
35593
  function applyFormat(num, pattern) {
35212
35594
  if (/^-?\d+$/.test(pattern.trim())) {
35213
35595
  return formatWithComma(num, Math.max(0, Number(pattern)));
@@ -35255,34 +35637,35 @@ function resolveFieldRef(row, field) {
35255
35637
  }
35256
35638
 
35257
35639
  // src/engine/evalWhere.ts
35258
- function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
35640
+ function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
35259
35641
  switch (expr.type) {
35260
35642
  case "BINARY":
35261
- return evalBinary(expr, row, resolveFieldType, appliedKlikes);
35643
+ return evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35262
35644
  case "NULL_CHECK":
35263
35645
  return evalNullCheck(expr, row);
35264
35646
  case "LOGICAL":
35265
- return evalLogical(expr, row, resolveFieldType, appliedKlikes);
35647
+ return evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35266
35648
  case "NOT":
35267
- return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
35649
+ return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35268
35650
  case "GROUP":
35269
- return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
35651
+ return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
35270
35652
  case "EXISTS": {
35271
35653
  const exists = expr.resolved;
35272
35654
  return expr.not ? !exists : exists;
35273
35655
  }
35274
35656
  }
35275
35657
  }
35276
- function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
35658
+ function evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
35277
35659
  if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
35278
35660
  if (appliedKlikes?.has(expr)) return true;
35279
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");
35280
35662
  }
35281
- const left = resolveField(expr.left, row, resolveFieldType);
35663
+ const left = resolveField(expr.left, row, resolveFieldType, resolveFieldSemantics2);
35282
35664
  const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
35283
- 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);
35284
35667
  }
35285
- function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
35668
+ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics = syntheticSemantics("string"), resolveFieldSemantics2) {
35286
35669
  if (op === "IN" || op === "NOT_IN") {
35287
35670
  let values = null;
35288
35671
  if (right.type === "IN_LIST") {
@@ -35307,8 +35690,53 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
35307
35690
  if (op === "KLIKE" || op === "NOT_KLIKE") {
35308
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");
35309
35692
  }
35310
- const rightStr = resolveValue(right, row, resolveFieldType);
35311
- 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");
35312
35740
  }
35313
35741
  var STRING_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
35314
35742
  var OBJECT_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([
@@ -35359,20 +35787,20 @@ function evalNullCheck(expr, row) {
35359
35787
  const val = resolveField(expr.field, row);
35360
35788
  return expr.not ? val !== "" : val === "";
35361
35789
  }
35362
- function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
35790
+ function evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
35363
35791
  if (expr.op === "AND") {
35364
- 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);
35365
35793
  }
35366
- 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);
35367
35795
  }
35368
- function resolveField(field, row, resolveFieldType) {
35796
+ function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
35369
35797
  if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
35370
35798
  if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
35371
- if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType);
35799
+ if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
35372
35800
  const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
35373
35801
  return resolveFieldRef(row, key);
35374
35802
  }
35375
- function resolveValue(value, row, resolveFieldType) {
35803
+ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
35376
35804
  switch (value.type) {
35377
35805
  case "VARIABLE":
35378
35806
  throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
@@ -35395,14 +35823,14 @@ function resolveValue(value, row, resolveFieldType) {
35395
35823
  if (value.expr.type === "STRING_FUNC") return evalStringFunc(value.expr, row);
35396
35824
  return String(evalArithExpr(value.expr, row));
35397
35825
  case "CASE_VALUE":
35398
- return evalCaseWhen(value.expr, row, resolveFieldType);
35826
+ return evalCaseWhen(value.expr, row, resolveFieldType, resolveFieldSemantics2);
35399
35827
  case "ARRAY":
35400
35828
  return value.elements.map((e) => e.value).join(",");
35401
35829
  }
35402
35830
  }
35403
- function evalCaseWhen(expr, row, resolveFieldType) {
35831
+ function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
35404
35832
  for (const branch of expr.branches) {
35405
- if (evalWhere(branch.condition, row, resolveFieldType)) {
35833
+ if (evalWhere(branch.condition, row, resolveFieldType, void 0, resolveFieldSemantics2)) {
35406
35834
  return evalCaseResult(branch.result, row);
35407
35835
  }
35408
35836
  }
@@ -36033,6 +36461,141 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
36033
36461
  };
36034
36462
  }
36035
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 planKorderNative(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 || stmt.limit < 0 || stmt.limit > 500) {
36577
+ reasons.push(`KORDER_LIMIT_INVALID(limit=${String(stmt.limit)})`);
36578
+ }
36579
+ if (stmt.limit !== null && stmt.limit > input.maxRecords) {
36580
+ reasons.push(`KORDER_LIMIT_EXCEEDS_MAX_RECORDS(limit=${stmt.limit}, maxRecords=${input.maxRecords})`);
36581
+ }
36582
+ const offset = stmt.offset ?? 0;
36583
+ if (offset < 0 || offset > 1e4) reasons.push(`KORDER_OFFSET_INVALID(offset=${offset})`);
36584
+ const unique = [...new Set(reasons)];
36585
+ if (unique.length > 0) {
36586
+ throw new Error(
36587
+ `ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
36588
+ );
36589
+ }
36590
+ return {
36591
+ kind: "KORDER_NATIVE",
36592
+ requiresCompleteInput: false,
36593
+ localOrderBy: false,
36594
+ applyLocalOffsetLimit: false,
36595
+ reasonCodes: []
36596
+ };
36597
+ }
36598
+
36036
36599
  // src/engine/process.ts
36037
36600
  function flatten(record2, alias) {
36038
36601
  const row = {};
@@ -36097,9 +36660,9 @@ function applyJoin(leftRows, rightRows, join) {
36097
36660
  }
36098
36661
  return result;
36099
36662
  }
36100
- function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
36663
+ function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
36101
36664
  if (where === null) return rows;
36102
- return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
36665
+ return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
36103
36666
  }
36104
36667
  function hasAggregateColumns(columns) {
36105
36668
  return columns.some(
@@ -36160,7 +36723,7 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
36160
36723
  let strVal;
36161
36724
  if (arg.type === "FIELD_REF") {
36162
36725
  const raw = row[arg.field];
36163
- if (raw === void 0 || raw === "") continue;
36726
+ if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
36164
36727
  strVal = raw;
36165
36728
  } else {
36166
36729
  const n = evalArithExpr(arg, row);
@@ -36172,10 +36735,16 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
36172
36735
  const eff = distinct ? [...new Set(strValues)] : strValues;
36173
36736
  if (func === "COUNT") return eff.length;
36174
36737
  if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
36175
- const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
36176
- if (sortKind === "string") {
36177
- if (eff.length === 0) return "";
36178
- return func === "MAX" ? maxStringOf(eff) : minStringOf(eff);
36738
+ const comparison = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
36739
+ if (func === "MIN" || func === "MAX") {
36740
+ if (eff.length === 0) return 0;
36741
+ const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" ? syntheticSemantics("string") : syntheticSemantics("number"));
36742
+ let result = eff[0];
36743
+ for (const candidate of eff.slice(1)) {
36744
+ const cmp = compareCanonicalValues(candidate, result, semantics);
36745
+ if (func === "MAX" && cmp > 0 || func === "MIN" && cmp < 0) result = candidate;
36746
+ }
36747
+ return result;
36179
36748
  }
36180
36749
  const nums = eff.map(Number);
36181
36750
  switch (func) {
@@ -36183,37 +36752,12 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
36183
36752
  return nums.reduce((a, b) => a + b, 0);
36184
36753
  case "AVG":
36185
36754
  return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length;
36186
- // Math.max(...nums) は要素数が多いと RangeError になるためループで求める
36187
- case "MAX":
36188
- return nums.length === 0 ? 0 : maxOf(nums);
36189
- case "MIN":
36190
- return nums.length === 0 ? 0 : minOf(nums);
36191
36755
  }
36192
36756
  }
36193
36757
  function toAggregateFieldRef(field) {
36194
36758
  const dot = field.indexOf(".");
36195
36759
  return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
36196
36760
  }
36197
- function maxStringOf(values) {
36198
- let value = values[0];
36199
- for (const candidate of values) if (candidate > value) value = candidate;
36200
- return value;
36201
- }
36202
- function minStringOf(values) {
36203
- let value = values[0];
36204
- for (const candidate of values) if (candidate < value) value = candidate;
36205
- return value;
36206
- }
36207
- function maxOf(nums) {
36208
- let m = nums[0];
36209
- for (const n of nums) if (n > m) m = n;
36210
- return m;
36211
- }
36212
- function minOf(nums) {
36213
- let m = nums[0];
36214
- for (const n of nums) if (n < m) m = n;
36215
- return m;
36216
- }
36217
36761
  function evalAggArithExpr(node, rows, resolveAggSortKind) {
36218
36762
  if (node.type === "NUMBER") return node.value;
36219
36763
  if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
@@ -36245,9 +36789,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
36245
36789
  const argStr = aggregateArgLabel(arg);
36246
36790
  return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
36247
36791
  }
36248
- function applyHaving(rows, having, resolveFieldType) {
36792
+ function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
36249
36793
  if (having === null) return rows;
36250
- return rows.filter((row) => evalWhere(having, row, resolveFieldType));
36794
+ return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
36251
36795
  }
36252
36796
  function applyDistinct(rows, columns) {
36253
36797
  if (rows.length === 0) return rows;
@@ -36299,27 +36843,37 @@ function buildDistinctKeyBuilder(rows, columns) {
36299
36843
  return JSON.stringify(values);
36300
36844
  };
36301
36845
  }
36302
- function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
36846
+ function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
36303
36847
  if (orderBy.length === 0) return rows;
36304
- return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
36305
- }
36306
- function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
36307
- const keyMeta = orderBy.map(({ key }) => ({
36308
- orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
36309
- sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
36310
- }));
36848
+ return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2).rows.map((item) => item.row);
36849
+ }
36850
+ function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
36851
+ const keyMeta = orderBy.map(({ key }) => {
36852
+ if (key.type === "ARITH_KEY") return { semantics: syntheticSemantics("number") };
36853
+ if (key.type === "FUNC_KEY") {
36854
+ return { semantics: syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(key.expr.func) ? "number" : "string") };
36855
+ }
36856
+ const semantics = fieldSemantics2?.get(key.name);
36857
+ if (semantics) return { semantics };
36858
+ const orderMap = optionOrders?.get(key.name);
36859
+ if (orderMap) {
36860
+ return {
36861
+ semantics: {
36862
+ fieldType: "MULTI_SELECT",
36863
+ compareMode: "option",
36864
+ inSubtable: false,
36865
+ requiresCollectionOperators: false,
36866
+ optionOrder: orderMap
36867
+ }
36868
+ };
36869
+ }
36870
+ return { semantics: syntheticSemantics(sortKinds?.get(key.name) ?? "string") };
36871
+ });
36311
36872
  const decorated = rows.map((row) => ({
36312
36873
  row,
36313
36874
  keys: orderBy.map(({ key }, i) => {
36314
36875
  const s = evalOrderKey(key, row);
36315
- const n = Number(s);
36316
- const orderMap = keyMeta[i].orderMap;
36317
- return {
36318
- s,
36319
- n,
36320
- isNum: !Number.isNaN(n),
36321
- rank: orderMap ? minChoiceIndex(parseChoiceValues(s), orderMap) : 0
36322
- };
36876
+ return { s };
36323
36877
  })
36324
36878
  }));
36325
36879
  const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
@@ -36334,15 +36888,24 @@ function compareDecoratedRows(a, b, orderBy, keyMeta) {
36334
36888
  return 0;
36335
36889
  }
36336
36890
  function compareSortKeys(a, b, meta3) {
36337
- if (meta3.orderMap) {
36338
- if (a.rank !== b.rank) return a.rank - b.rank;
36339
- return a.s.localeCompare(b.s, "ja");
36340
- }
36341
- if (meta3.sortKind === "string") {
36342
- return a.s.localeCompare(b.s, "ja");
36343
- }
36344
- return a.isNum && b.isNum ? a.n - b.n : a.s.localeCompare(b.s, "ja");
36345
- }
36891
+ return compareCanonicalValues(a.s, b.s, meta3.semantics);
36892
+ }
36893
+ var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
36894
+ "LENGTH",
36895
+ "INSTR",
36896
+ "ROUND",
36897
+ "FLOOR",
36898
+ "CEIL",
36899
+ "TRUNCATE",
36900
+ "YEAR",
36901
+ "MONTH",
36902
+ "DAY",
36903
+ "DATEDIFF",
36904
+ "ABS",
36905
+ "MOD",
36906
+ "POWER",
36907
+ "SQRT"
36908
+ ]);
36346
36909
  function evalOrderKey(key, row) {
36347
36910
  switch (key.type) {
36348
36911
  case "FIELD_NAME":
@@ -36353,30 +36916,7 @@ function evalOrderKey(key, row) {
36353
36916
  return evalStringFunc(key.expr, row);
36354
36917
  }
36355
36918
  }
36356
- function parseChoiceValues(raw) {
36357
- const trimmed = raw.trim();
36358
- if (trimmed === "") return [""];
36359
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
36360
- try {
36361
- const arr = JSON.parse(trimmed);
36362
- if (Array.isArray(arr)) {
36363
- return arr.map((v) => String(v ?? ""));
36364
- }
36365
- } catch {
36366
- }
36367
- }
36368
- return [trimmed];
36369
- }
36370
- function minChoiceIndex(values, orderMap) {
36371
- let min = Number.MAX_SAFE_INTEGER;
36372
- for (const value of values) {
36373
- const idx = orderMap.get(value);
36374
- const rank = idx ?? Number.MAX_SAFE_INTEGER;
36375
- if (rank < min) min = rank;
36376
- }
36377
- return min;
36378
- }
36379
- function applyWindow(rows, columns, optionOrders, sortKinds) {
36919
+ function applyWindow(rows, columns, optionOrders, sortKinds, fieldSemantics2) {
36380
36920
  const windows = columns.filter((column) => column.type === "WINDOW_COL");
36381
36921
  if (rows.length === 0 || windows.length === 0) return rows;
36382
36922
  for (const window of windows) {
@@ -36388,7 +36928,7 @@ function applyWindow(rows, columns, optionOrders, sortKinds) {
36388
36928
  else partitions.set(key, [row]);
36389
36929
  }
36390
36930
  for (const partition of partitions.values()) {
36391
- const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
36931
+ const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
36392
36932
  const sorted = sortedResult.rows;
36393
36933
  let rank = 1;
36394
36934
  let denseRank = 1;
@@ -36413,7 +36953,7 @@ function applyLimit(rows, limit, offset) {
36413
36953
  if (limit === null) return rows.slice(start);
36414
36954
  return rows.slice(start, start + limit);
36415
36955
  }
36416
- function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
36956
+ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2) {
36417
36957
  if (columns.length === 1 && columns[0].type === "WILDCARD") {
36418
36958
  const projected2 = rows.map((row) => stripParentShortcutColumns(row));
36419
36959
  const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
@@ -36477,7 +37017,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
36477
37017
  }
36478
37018
  case "CASE_COL": {
36479
37019
  const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
36480
- out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
37020
+ out[key] = evalCaseWhen(col.expr, row, resolveFieldType, resolveFieldSemantics2);
36481
37021
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
36482
37022
  break;
36483
37023
  }
@@ -36632,6 +37172,26 @@ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
36632
37172
  args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
36633
37173
  };
36634
37174
  }
37175
+ function deriveOutputOrderSemantics(columns) {
37176
+ const result = /* @__PURE__ */ new Map();
37177
+ for (const column of columns) {
37178
+ if (!("alias" in column) || !column.alias) continue;
37179
+ if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
37180
+ result.set(column.alias, syntheticSemantics("number"));
37181
+ } else if (column.type === "AGGREGATE") {
37182
+ if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
37183
+ result.set(column.alias, syntheticSemantics("number"));
37184
+ } else if (column.func === "GROUP_CONCAT") {
37185
+ result.set(column.alias, syntheticSemantics("string"));
37186
+ }
37187
+ } else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
37188
+ result.set(column.alias, syntheticSemantics("string"));
37189
+ } else if (column.type === "STRFUNC_COL") {
37190
+ result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
37191
+ }
37192
+ }
37193
+ return result;
37194
+ }
36635
37195
  function runFullScan(input) {
36636
37196
  const {
36637
37197
  stmt,
@@ -36639,12 +37199,17 @@ function runFullScan(input) {
36639
37199
  scalarCache,
36640
37200
  optionOrders,
36641
37201
  sortKinds,
37202
+ orderSemantics,
36642
37203
  fieldTypeResolver,
37204
+ fieldSemanticsResolver,
36643
37205
  havingFieldTypeResolver,
37206
+ havingFieldSemanticsResolver,
36644
37207
  aggregateSortKindResolver,
36645
37208
  appliedKlikes,
36646
37209
  sourceColumns
36647
37210
  } = input;
37211
+ const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
37212
+ for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
36648
37213
  let rows = [];
36649
37214
  const mainAlias = stmt.from.alias;
36650
37215
  const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
@@ -36655,18 +37220,18 @@ function runFullScan(input) {
36655
37220
  const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
36656
37221
  rows = applyJoin(rows, rightRows, join);
36657
37222
  }
36658
- rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
37223
+ rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
36659
37224
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
36660
37225
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
36661
37226
  }
36662
- rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
36663
- rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
37227
+ rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
37228
+ rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
36664
37229
  if (stmt.distinct) {
36665
37230
  rows = applyDistinct(rows, stmt.columns);
36666
37231
  }
36667
- rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
37232
+ rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds, effectiveOrderSemantics);
36668
37233
  rows = applyLimit(rows, stmt.limit, stmt.offset);
36669
- return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
37234
+ return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns, fieldSemanticsResolver);
36670
37235
  }
36671
37236
 
36672
37237
  // src/converter/subtableAdapter.ts
@@ -36934,6 +37499,192 @@ function renderValidationValue(value) {
36934
37499
  return String(value);
36935
37500
  }
36936
37501
 
37502
+ // src/core/optimization/whereCapability.ts
37503
+ var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
37504
+ var EQUALITY_IN = ["=", "!=", "in", "not in"];
37505
+ var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
37506
+ ["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37507
+ ["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37508
+ ["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
37509
+ ["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
37510
+ ["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
37511
+ ["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
37512
+ ["DATE", new Set(RANGE_AND_EQUALITY)],
37513
+ ["TIME", new Set(RANGE_AND_EQUALITY)],
37514
+ ["DATETIME", new Set(RANGE_AND_EQUALITY)],
37515
+ ["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
37516
+ ["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
37517
+ ["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37518
+ ["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
37519
+ ["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
37520
+ ["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
37521
+ ["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
37522
+ ["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
37523
+ ["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
37524
+ ["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37525
+ ["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
37526
+ ["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37527
+ ["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37528
+ ["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
37529
+ ["STATUS", new Set(EQUALITY_IN)]
37530
+ ]);
37531
+ var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
37532
+ "RECORD_NUMBER",
37533
+ "__ID__",
37534
+ "CREATOR",
37535
+ "MODIFIER",
37536
+ "CREATED_TIME",
37537
+ "UPDATED_TIME",
37538
+ "DATE",
37539
+ "TIME",
37540
+ "DATETIME",
37541
+ "SINGLE_LINE_TEXT",
37542
+ "LINK",
37543
+ "NUMBER",
37544
+ "CALC",
37545
+ "MULTI_LINE_TEXT",
37546
+ "RICH_TEXT",
37547
+ "RADIO_BUTTON",
37548
+ "DROP_DOWN",
37549
+ "STATUS",
37550
+ // 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
37551
+ "KSQL_STRING",
37552
+ "KSQL_NUMBER",
37553
+ "KSQL_BOOLEAN"
37554
+ ]);
37555
+ var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
37556
+ "CHECK_BOX",
37557
+ "MULTI_SELECT",
37558
+ "FILE",
37559
+ "USER_SELECT",
37560
+ "ORGANIZATION_SELECT",
37561
+ "GROUP_SELECT",
37562
+ "STATUS_ASSIGNEE",
37563
+ "CATEGORY"
37564
+ ]);
37565
+ function nativeWhereOperatorsForType(fieldType) {
37566
+ return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
37567
+ }
37568
+ function classifyWhereCapability(where, resolveField2) {
37569
+ if (where === null) {
37570
+ return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
37571
+ }
37572
+ return classifyNode(where, resolveField2);
37573
+ }
37574
+ function classifyNode(where, resolveField2) {
37575
+ switch (where.type) {
37576
+ case "BINARY":
37577
+ return classifyBinary(where.op, where.left, where.right.type, resolveField2);
37578
+ case "NULL_CHECK":
37579
+ if (where.field.type !== "FIELD") return localExpression();
37580
+ return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
37581
+ case "EXISTS":
37582
+ return localExpression();
37583
+ case "GROUP":
37584
+ return classifyNode(where.expr, resolveField2);
37585
+ case "NOT": {
37586
+ const inner = classifyNode(where.expr, resolveField2);
37587
+ return inner.capability === "SUPERSET_PREFILTER" ? { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] } : inner;
37588
+ }
37589
+ case "LOGICAL": {
37590
+ const left = classifyNode(where.left, resolveField2);
37591
+ const right = classifyNode(where.right, resolveField2);
37592
+ return combineLogical(where.op, left, right);
37593
+ }
37594
+ }
37595
+ }
37596
+ function classifyBinary(op, left, rightType, resolveField2) {
37597
+ if (left.type !== "FIELD") return localExpression();
37598
+ const semantics = resolveField2(left);
37599
+ if (!semantics) {
37600
+ return unsupported("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
37601
+ }
37602
+ if (!hasLocalContract(semantics.fieldType, op)) {
37603
+ return unsupported("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, normalizeOperator(op));
37604
+ }
37605
+ const nativeOp = normalizeOperator(op);
37606
+ const native = nativeWhereOperatorsForType(semantics.fieldType);
37607
+ const rightCanPush = rightType === "STRING" || rightType === "NUMBER" || rightType === "IN_LIST" || rightType === "KINTONE_FUNC";
37608
+ const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
37609
+ const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
37610
+ if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
37611
+ return {
37612
+ capability: "EXACT_PUSHDOWN",
37613
+ reasons: [{
37614
+ code: "WHERE_EXACT",
37615
+ field: left.field,
37616
+ fieldType: semantics.fieldType,
37617
+ operator: nativeOp
37618
+ }]
37619
+ };
37620
+ }
37621
+ return {
37622
+ capability: "LOCAL_ONLY",
37623
+ reasons: [{
37624
+ code: "WHERE_RESIDUAL",
37625
+ field: left.field,
37626
+ fieldType: semantics.fieldType,
37627
+ operator: nativeOp
37628
+ }]
37629
+ };
37630
+ }
37631
+ function classifyLocalOnlyField(field, operator, resolveField2) {
37632
+ const semantics = resolveField2(field);
37633
+ if (!semantics) return unsupported("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
37634
+ if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
37635
+ return unsupported("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
37636
+ }
37637
+ return {
37638
+ capability: "LOCAL_ONLY",
37639
+ reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
37640
+ };
37641
+ }
37642
+ function hasLocalContract(fieldType, op) {
37643
+ if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
37644
+ if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
37645
+ return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
37646
+ }
37647
+ function normalizeOperator(op) {
37648
+ switch (op) {
37649
+ case "<>":
37650
+ return "!=";
37651
+ case "IN":
37652
+ return "in";
37653
+ case "NOT_IN":
37654
+ return "not in";
37655
+ case "LIKE":
37656
+ case "KLIKE":
37657
+ return "like";
37658
+ case "NOT_LIKE":
37659
+ case "NOT_KLIKE":
37660
+ return "not like";
37661
+ default:
37662
+ return op;
37663
+ }
37664
+ }
37665
+ function combineLogical(op, left, right) {
37666
+ const reasons = [...left.reasons, ...right.reasons];
37667
+ if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
37668
+ return { capability: "UNSUPPORTED", reasons };
37669
+ }
37670
+ if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
37671
+ return { capability: "EXACT_PUSHDOWN", reasons };
37672
+ }
37673
+ if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
37674
+ return {
37675
+ capability: "SUPERSET_PREFILTER",
37676
+ reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
37677
+ };
37678
+ }
37679
+ return { capability: "LOCAL_ONLY", reasons };
37680
+ }
37681
+ function localExpression() {
37682
+ return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
37683
+ }
37684
+ function unsupported(code, field, fieldType, operator) {
37685
+ return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
37686
+ }
37687
+
36937
37688
  // src/execute.ts
36938
37689
  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";
36939
37690
  var SearchAbortedError = class extends Error {
@@ -36944,8 +37695,20 @@ var SearchAbortedError = class extends Error {
36944
37695
  };
36945
37696
  var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
36946
37697
  var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
37698
+ var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
37699
+ var nextDefaultCacheContextId = 1;
37700
+ function resolveCacheContext(client, explicit) {
37701
+ if (explicit) return explicit;
37702
+ let context = defaultCacheContextByClient.get(client);
37703
+ if (!context) {
37704
+ context = `client:${nextDefaultCacheContextId++}`;
37705
+ defaultCacheContextByClient.set(client, context);
37706
+ }
37707
+ return context;
37708
+ }
36947
37709
  async function execute(sql, client, options = {}) {
36948
37710
  const startedAt = Date.now();
37711
+ const cacheContext = resolveCacheContext(client, options.cacheContext);
36949
37712
  const stmt = parseSql(sql);
36950
37713
  const metrics = createEmptyMetrics();
36951
37714
  const countedClient = wrapClientWithMetrics(client, metrics);
@@ -36959,7 +37722,7 @@ async function execute(sql, client, options = {}) {
36959
37722
  stmt,
36960
37723
  guardedClient,
36961
37724
  options,
36962
- options.cacheContext ?? "default"
37725
+ cacheContext
36963
37726
  );
36964
37727
  metrics.elapsedMs = Date.now() - startedAt;
36965
37728
  return { ...attachSearchAbortWarning(result, collector), metrics };
@@ -37074,7 +37837,7 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
37074
37837
  case "DESCRIBE":
37075
37838
  return executeDescribe(stmt, client, cacheContext);
37076
37839
  case "EXPLAIN":
37077
- return executeExplain(stmt);
37840
+ return executeExplain(stmt, client, cacheContext, options.maxRecords ?? 1e4);
37078
37841
  // 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
37079
37842
  case "CREATE_TEMP_TABLE":
37080
37843
  throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
@@ -37105,7 +37868,7 @@ function materializedColumnMetaEqual(left, right) {
37105
37868
  if (!left || !right || left.size !== right.size) return false;
37106
37869
  for (const [column, meta3] of left) {
37107
37870
  const candidate = right.get(column);
37108
- if (!candidate || candidate.sortKind !== meta3.sortKind || candidate.fieldType !== meta3.fieldType) return false;
37871
+ if (!candidate || candidate.sortKind !== meta3.sortKind || candidate.fieldType !== meta3.fieldType || !fieldSemanticsEqual(candidate.semantics, meta3.semantics)) return false;
37109
37872
  }
37110
37873
  return true;
37111
37874
  }
@@ -37136,7 +37899,7 @@ async function executeBatch(sql, client, options = {}) {
37136
37899
  const countedClient = wrapClientWithMetrics(client, metrics);
37137
37900
  const startedAt = Date.now();
37138
37901
  const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
37139
- const cacheContext = options.cacheContext ?? "default";
37902
+ const cacheContext = resolveCacheContext(client, options.cacheContext);
37140
37903
  const tempTables = /* @__PURE__ */ new Map();
37141
37904
  const variables = /* @__PURE__ */ new Map();
37142
37905
  const results = [];
@@ -37230,7 +37993,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
37230
37993
  cacheContext,
37231
37994
  tempTables
37232
37995
  );
37233
- variables.set(stmt.name, { type: "string", value });
37996
+ const first = resolvedStmt2.expr.query.columns[0];
37997
+ 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");
37998
+ const numberValue = numeric ? Number(value) : Number.NaN;
37999
+ variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
37234
38000
  } catch (e) {
37235
38001
  if (e instanceof ScalarSubqueryError) {
37236
38002
  throw new Error(`ArgumentError: ${e.message}`);
@@ -37461,13 +38227,14 @@ var ScalarSubqueryError = class extends Error {
37461
38227
  };
37462
38228
  async function executeAssert(stmt, client, options, cacheContext, tempTables) {
37463
38229
  const left = await evalAssertOperand(stmt.left, client, options, cacheContext, tempTables);
38230
+ const semantics = stmt.left.type === "NUMBER" || stmt.left.type === "ARITH" ? syntheticSemantics("number") : syntheticSemantics("string");
37464
38231
  if (stmt.op === "BETWEEN") {
37465
38232
  if (stmt.low === null || stmt.high === null) {
37466
38233
  throw new Error("ArgumentError: malformed ASSERT statement.");
37467
38234
  }
37468
38235
  const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
37469
38236
  const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
37470
- if (!compareScalarValues(">=", left, low) || !compareScalarValues("<=", left, high)) {
38237
+ if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
37471
38238
  throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
37472
38239
  }
37473
38240
  return { type: "ASSERT", condition: stmt.text };
@@ -37476,7 +38243,7 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
37476
38243
  throw new Error("ArgumentError: malformed ASSERT statement.");
37477
38244
  }
37478
38245
  const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
37479
- if (!compareScalarValues(stmt.op, left, right)) {
38246
+ if (!compareScalarValues(stmt.op, left, right, semantics)) {
37480
38247
  throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
37481
38248
  }
37482
38249
  return { type: "ASSERT", condition: stmt.text };
@@ -37549,6 +38316,149 @@ function evalAssertArith(node) {
37549
38316
  }
37550
38317
  throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
37551
38318
  }
38319
+ async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables, forcePhysicalMetadata = false) {
38320
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
38321
+ const physicalAppIds = forcePhysicalMetadata || whereNeedsFieldMetadata(stmt.where) ? [...new Set(tables.filter((table) => table.cteName === null).map((table) => table.appId))] : [];
38322
+ const infosByApp = new Map(
38323
+ await Promise.all(physicalAppIds.map(async (appId) => {
38324
+ const infos = await getFieldsCached(appId, client, cacheContext);
38325
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
38326
+ }))
38327
+ );
38328
+ const orderedFields = /* @__PURE__ */ new Set();
38329
+ const collectOrderedFields = (node) => {
38330
+ if (Array.isArray(node)) {
38331
+ node.forEach(collectOrderedFields);
38332
+ return;
38333
+ }
38334
+ if (node === null || typeof node !== "object") return;
38335
+ const value = node;
38336
+ if (value["type"] === "SELECT") return;
38337
+ if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
38338
+ const left = value["left"];
38339
+ if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
38340
+ orderedFields.add(left["field"]);
38341
+ }
38342
+ }
38343
+ Object.values(value).forEach(collectOrderedFields);
38344
+ };
38345
+ collectOrderedFields(stmt.where);
38346
+ collectOrderedFields(stmt.having);
38347
+ for (const column of stmt.columns) {
38348
+ if (column.type === "CASE_COL") collectOrderedFields(column.expr);
38349
+ }
38350
+ const statusOrdersByApp = /* @__PURE__ */ new Map();
38351
+ await Promise.all([...infosByApp].map(async ([appId, infos]) => {
38352
+ const needsStatus = [...orderedFields].some((field) => infos.get(field)?.fieldType === "STATUS");
38353
+ if (!needsStatus) return;
38354
+ const order = await loadProcessStatusOrder(appId, client, cacheContext);
38355
+ if (order) statusOrdersByApp.set(appId, order);
38356
+ }));
38357
+ const fromPhysical = (table, field) => {
38358
+ if (field === "$id") return withFieldSemanticSource(
38359
+ resolveFieldSemantics({ fieldType: "__ID__" }),
38360
+ table.appId,
38361
+ "$id"
38362
+ );
38363
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field));
38364
+ if (!info) return void 0;
38365
+ const base = info.semantics ?? resolveFieldSemantics(info);
38366
+ const semantics = info.fieldType === "STATUS" && statusOrdersByApp.has(table.appId) ? { ...base, optionOrder: statusOrdersByApp.get(table.appId) } : base;
38367
+ return withFieldSemanticSource(
38368
+ semantics,
38369
+ table.appId,
38370
+ info.code
38371
+ );
38372
+ };
38373
+ return (field) => {
38374
+ if (field.tableAlias !== null) {
38375
+ if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
38376
+ return fromPhysical(stmt.from, field.field);
38377
+ }
38378
+ const table = tables.find((candidate) => candidate.alias === field.tableAlias);
38379
+ if (!table) return void 0;
38380
+ if (table.cteName !== null) {
38381
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
38382
+ }
38383
+ return fromPhysical(table, field.field);
38384
+ }
38385
+ if (stmt.joins.length === 0) {
38386
+ if (stmt.from.cteName !== null) {
38387
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
38388
+ }
38389
+ return fromPhysical(stmt.from, field.field);
38390
+ }
38391
+ const matches = tables.flatMap((table) => {
38392
+ const semantics = table.cteName !== null ? materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics : fromPhysical(table, field.field);
38393
+ return semantics ? [semantics] : [];
38394
+ });
38395
+ if (matches.length === 1) return matches[0];
38396
+ return matches.length > 1 ? syntheticSemantics("string") : void 0;
38397
+ };
38398
+ }
38399
+ function selectCaseConditionsNeedFieldMetadata(stmt) {
38400
+ return stmt.columns.some((column) => column.type === "CASE_COL" && column.expr.branches.some((branch) => whereNeedsFieldMetadata(branch.condition)));
38401
+ }
38402
+ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
38403
+ const aliases = /* @__PURE__ */ new Map();
38404
+ for (const column of stmt.columns) {
38405
+ if (!("alias" in column) || !column.alias) continue;
38406
+ let semantics;
38407
+ if (column.type === "FIELD") semantics = rowResolver(aggregateFieldRef(column.field));
38408
+ else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
38409
+ semantics = syntheticSemantics("number");
38410
+ } else if (column.type === "AGGREGATE") {
38411
+ if (column.func === "MIN" || column.func === "MAX") {
38412
+ semantics = column.arg.type === "FIELD_REF" ? rowResolver(aggregateFieldRef(column.arg.field)) : syntheticSemantics("number");
38413
+ } else {
38414
+ semantics = column.func === "GROUP_CONCAT" ? syntheticSemantics("string") : syntheticSemantics("number");
38415
+ }
38416
+ } else if (column.type === "STRFUNC_COL") {
38417
+ semantics = stringFunctionColumnMeta(column.expr).semantics;
38418
+ } else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
38419
+ semantics = syntheticSemantics("string");
38420
+ }
38421
+ if (semantics) aliases.set(column.alias, semantics);
38422
+ }
38423
+ return (field) => field.tableAlias === null && aliases.has(field.field) ? aliases.get(field.field) : rowResolver(field);
38424
+ }
38425
+ async function resolveSelectWhereCapability(stmt, client, cacheContext, materializedTables) {
38426
+ if (stmt.where === null) return classifyWhereCapability(null, () => void 0);
38427
+ const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables);
38428
+ return classifyWhereCapability(stmt.where, resolver);
38429
+ }
38430
+ function formatWhereCapabilityFailure(result) {
38431
+ const reason = result.reasons.find(
38432
+ (candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED"
38433
+ ) ?? result.reasons[0];
38434
+ const details = [
38435
+ reason?.field ? `field=${reason.field}` : null,
38436
+ reason?.fieldType ? `type=${reason.fieldType}` : null,
38437
+ reason?.operator ? `operator=${reason.operator}` : null,
38438
+ reason?.code ? `reason=${reason.code}` : null
38439
+ ].filter((value) => value !== null).join(", ");
38440
+ return details || "reason=WHERE_UNSUPPORTED";
38441
+ }
38442
+ function hasCanonicalOrder(stmt) {
38443
+ return stmt.orderBy.length > 0 || stmt.columns.some(
38444
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
38445
+ );
38446
+ }
38447
+ async function assertDmlWhereCapability(stmt, client, cacheContext) {
38448
+ if (stmt.subtableCode || stmt.type === "UPDATE" && stmt.from != null) return;
38449
+ const fields = whereNeedsFieldMetadata(stmt.where) ? await getFieldsCached(stmt.appId, client, cacheContext) : [];
38450
+ const byCode = new Map(fields.map((field) => [field.code, field]));
38451
+ const result = classifyWhereCapability(stmt.where, (field) => {
38452
+ if (field.field === "$id") return resolveFieldSemantics({ fieldType: "__ID__" });
38453
+ const info = byCode.get(field.field);
38454
+ return info?.semantics ?? (info ? resolveFieldSemantics(info) : void 0);
38455
+ });
38456
+ if (result.capability !== "EXACT_PUSHDOWN") {
38457
+ throw new DmlConvertError(
38458
+ `WHERE predicate cannot be represented by kintone REST (${formatWhereCapabilityFailure(result)})`
38459
+ );
38460
+ }
38461
+ }
37552
38462
  async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
37553
38463
  let result;
37554
38464
  if (isNoFromSelect(stmt)) {
@@ -37559,12 +38469,51 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
37559
38469
  return result;
37560
38470
  }
37561
38471
  await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
37562
- const mode = resolveSelectMode(stmt);
37563
- await validateSelectFieldCodes(stmt, mode, client, cacheContext);
37564
- if (mode === "SIMPLE") {
37565
- result = await executeSimpleSelect(stmt, client, options, cacheContext);
37566
- } else {
37567
- result = await executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
38472
+ const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
38473
+ if (whereCapability.capability === "UNSUPPORTED") {
38474
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
38475
+ }
38476
+ const staticMode = resolveSelectMode(stmt);
38477
+ const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
38478
+ const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
38479
+ const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
38480
+ stmt,
38481
+ staticMode: mode,
38482
+ whereCapability: whereCapability.capability,
38483
+ orderSemantics: orderMeta.semantics,
38484
+ maxRecords: options.maxRecords ?? 1e4,
38485
+ hasKlike: whereHasKlike(stmt.where)
38486
+ }) : null;
38487
+ await validateSelectFieldCodes(
38488
+ stmt,
38489
+ orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : mode,
38490
+ client,
38491
+ cacheContext
38492
+ );
38493
+ const completeInputRequired = orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" ? requiresCompleteInput({ ...stmt, orderBy: [] }) : requiresCompleteInput(stmt);
38494
+ const truncateWasDisabled = completeInputRequired && options.onLimitReached === "truncate";
38495
+ const effectiveOptions = truncateWasDisabled ? { ...options, onLimitReached: "error" } : options;
38496
+ try {
38497
+ if (mode === "SIMPLE") {
38498
+ result = await executeSimpleSelect(stmt, client, effectiveOptions, cacheContext, orderPlan, orderMeta);
38499
+ } else {
38500
+ result = await executeFullScanSelect(
38501
+ stmt,
38502
+ client,
38503
+ effectiveOptions,
38504
+ cacheContext,
38505
+ cteCache,
38506
+ whereCapability.capability === "EXACT_PUSHDOWN",
38507
+ orderMeta
38508
+ );
38509
+ }
38510
+ } catch (error51) {
38511
+ if (completeInputRequired && error51 instanceof FetchAllLimitError) {
38512
+ throw new FetchAllLimitError(
38513
+ "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
38514
+ );
38515
+ }
38516
+ throw error51;
37568
38517
  }
37569
38518
  if (captureColumnMeta) {
37570
38519
  materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
@@ -37625,19 +38574,30 @@ function executeNoFromSelect(stmt) {
37625
38574
  const rows = applyLimit(projected, stmt.limit, stmt.offset);
37626
38575
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
37627
38576
  }
37628
- async function executeSimpleSelect(stmt, client, options, cacheContext) {
37629
- const params = selectToKintoneParams(stmt);
38577
+ async function executeSimpleSelect(stmt, client, options, cacheContext, orderPlan, orderMeta) {
38578
+ const restStmt = orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt;
38579
+ const params = selectToKintoneParams(restStmt);
38580
+ const fetchFields = orderPlan?.kind === "CANONICAL_LOCAL" ? selectToFetchAllFields(stmt, stmt.from) : params.fields;
37630
38581
  const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
37631
38582
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
38583
+ const projectionSemanticsResolver = stmt.columns.some((column) => column.type === "CASE_COL") ? await buildWhereFieldSemanticsResolver(
38584
+ stmt,
38585
+ client,
38586
+ cacheContext,
38587
+ void 0,
38588
+ selectCaseConditionsNeedFieldMetadata(stmt)
38589
+ ) : void 0;
37632
38590
  const maxRecords2 = options.maxRecords ?? 1e4;
37633
38591
  const warnings = /* @__PURE__ */ new Set();
37634
38592
  const onLimit2 = options.onLimitReached ?? "error";
37635
38593
  const parallel = options.fetchParallel ?? 1;
37636
- const useSingleGet = stmt.limit !== null && stmt.limit <= 500;
38594
+ const useRestWindow = stmt.orderBy.length > 0 ? orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" : stmt.limit !== null && stmt.limit <= 500;
37637
38595
  const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
37638
38596
  const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords2 && !whereHasKlike(stmt.where) ? needed : void 0;
37639
38597
  let records;
37640
- if (useSingleGet) {
38598
+ if (orderPlan?.kind === "KORDER_NATIVE" && stmt.limit === 0) {
38599
+ records = [];
38600
+ } else if (useRestWindow) {
37641
38601
  const res = await client.getRecords({
37642
38602
  app: params.app,
37643
38603
  query: params.query,
@@ -37650,7 +38610,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
37650
38610
  client.getRecords,
37651
38611
  params.app,
37652
38612
  baseQuery,
37653
- params.fields,
38613
+ fetchFields,
37654
38614
  {
37655
38615
  parallel,
37656
38616
  maxRecords: maxRecords2,
@@ -37663,16 +38623,23 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
37663
38623
  );
37664
38624
  }
37665
38625
  let rows = records.map((r) => flatten(r, null));
37666
- if (!useSingleGet) {
37667
- const { optionOrders, sortKinds } = await buildOrderByMetaForSelect(stmt, client, cacheContext);
37668
- rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
38626
+ if (!useRestWindow) {
38627
+ rows = applyOrderBy(
38628
+ rows,
38629
+ stmt.orderBy,
38630
+ orderMeta.optionOrders,
38631
+ orderMeta.sortKinds,
38632
+ orderMeta.semantics
38633
+ );
37669
38634
  rows = applyLimit(rows, stmt.limit, stmt.offset);
37670
38635
  }
37671
38636
  const { rows: projected, columns } = project(
37672
38637
  rows,
37673
38638
  stmt.columns,
37674
38639
  void 0,
37675
- fieldTypeResolvers.row
38640
+ fieldTypeResolvers.row,
38641
+ void 0,
38642
+ projectionSemanticsResolver
37676
38643
  );
37677
38644
  return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
37678
38645
  }
@@ -37745,8 +38712,8 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
37745
38712
  const statusFields = collectCandidateFieldCodes(candidates).filter((fieldCode) => fieldTypes.get(fieldCode) === "STATUS");
37746
38713
  if (statusFields.length > 0) {
37747
38714
  const process4 = await getProcessStatusesCached(appId, client, cacheContext);
37748
- if (process4.enable && process4.states.length > 0) {
37749
- const states = new Set(process4.states);
38715
+ if (process4.enable && process4.states && process4.states.length > 0) {
38716
+ const states = new Set(process4.states.map((state) => state.name));
37750
38717
  for (const fieldCode of statusFields) fieldOptions.set(fieldCode, states);
37751
38718
  }
37752
38719
  }
@@ -37924,7 +38891,18 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
37924
38891
  return [appId, new Map(infos.map((info) => [info.code, info]))];
37925
38892
  }))
37926
38893
  );
38894
+ const statusOrdersByApp = /* @__PURE__ */ new Map();
38895
+ const aggregateFieldNames = new Set(refs.map((ref) => ref.field));
38896
+ await Promise.all([...fieldInfosByApp].map(async ([appId, infos]) => {
38897
+ if (![...aggregateFieldNames].some((field) => infos.get(field)?.fieldType === "STATUS")) return;
38898
+ const order = await loadProcessStatusOrder(appId, client, cacheContext);
38899
+ if (order) statusOrdersByApp.set(appId, order);
38900
+ }));
37927
38901
  const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
38902
+ const semanticsForInfo = (info, appId) => {
38903
+ const base = info.semantics ?? resolveFieldSemantics(info);
38904
+ return info.fieldType === "STATUS" && statusOrdersByApp.has(appId) ? { ...base, optionOrder: statusOrdersByApp.get(appId) } : base;
38905
+ };
37928
38906
  return (ref) => {
37929
38907
  let info;
37930
38908
  if (ref.tableAlias !== null) {
@@ -37934,40 +38912,130 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
37934
38912
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
37935
38913
  if (!table) return void 0;
37936
38914
  if (table.cteName !== null) {
37937
- return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.sortKind;
38915
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
37938
38916
  }
37939
38917
  info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37940
38918
  }
37941
38919
  } else if (stmt.joins.length === 0) {
37942
38920
  if (stmt.from.cteName !== null) {
37943
- return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.sortKind;
38921
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
37944
38922
  }
37945
38923
  info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
37946
38924
  } else {
37947
38925
  const matches = tables.flatMap((table) => {
37948
38926
  if (table.cteName !== null) {
37949
38927
  const materialized = materializedTables?.get(table.cteName);
37950
- return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.sortKind] : [];
38928
+ return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string")] : [];
37951
38929
  }
37952
38930
  const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37953
- return candidate ? [aggregateSortKind(candidate)] : [];
38931
+ return candidate ? [semanticsForInfo(candidate, table.appId)] : [];
37954
38932
  });
37955
38933
  if (matches.length !== 1) return void 0;
37956
38934
  return matches[0];
37957
38935
  }
37958
- return info ? aggregateSortKind(info) : void 0;
38936
+ if (!info) return void 0;
38937
+ const sourceTable = ref.tableAlias !== null ? tables.find((table) => table.alias === ref.tableAlias) : stmt.joins.length === 0 ? stmt.from : void 0;
38938
+ return semanticsForInfo(info, sourceTable?.appId ?? stmt.from.appId);
37959
38939
  };
37960
38940
  }
37961
38941
  function fieldCodeForTypeLookup(table, field) {
37962
38942
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
37963
38943
  return field;
37964
38944
  }
37965
- function materializedMetaFromFieldInfo(info) {
37966
- return { sortKind: aggregateSortKind(info), fieldType: info.fieldType };
38945
+ function materializedMetaFromFieldInfo(info, sourceAppId) {
38946
+ const semantics = info.semantics ?? resolveFieldSemantics(info);
38947
+ return {
38948
+ sortKind: aggregateSortKind(info),
38949
+ fieldType: info.fieldType,
38950
+ semantics: sourceAppId === void 0 ? semantics : withFieldSemanticSource(semantics, sourceAppId, info.code)
38951
+ };
38952
+ }
38953
+ function withCanonicalRestTie(stmt) {
38954
+ const hasId = stmt.orderBy.some(
38955
+ (item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
38956
+ );
38957
+ return hasId ? stmt : {
38958
+ ...stmt,
38959
+ orderBy: [...stmt.orderBy, { key: { type: "FIELD_NAME", name: "$id" }, direction: "ASC" }]
38960
+ };
38961
+ }
38962
+ function syntheticColumnMeta(compareMode) {
38963
+ return { sortKind: compareMode, semantics: syntheticSemantics(compareMode) };
38964
+ }
38965
+ function unknownStringColumnMeta() {
38966
+ return { semantics: syntheticSemantics("string", "KSQL_UNKNOWN") };
38967
+ }
38968
+ function unsupportedColumnMeta(fieldType = "KSQL_ARRAY") {
38969
+ return {
38970
+ semantics: { fieldType, compareMode: "unsupported", inSubtable: false, requiresCollectionOperators: false }
38971
+ };
38972
+ }
38973
+ function systemColumnMeta(field) {
38974
+ if (field === "$id" || field === "_rid" || field === "_pid") {
38975
+ return {
38976
+ sortKind: "number",
38977
+ fieldType: "__ID__",
38978
+ semantics: resolveFieldSemantics({ fieldType: "__ID__" })
38979
+ };
38980
+ }
38981
+ if (field === "$revision") return syntheticColumnMeta("number");
38982
+ return void 0;
38983
+ }
38984
+ var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
38985
+ "LENGTH",
38986
+ "INSTR",
38987
+ "ROUND",
38988
+ "FLOOR",
38989
+ "CEIL",
38990
+ "TRUNCATE",
38991
+ "YEAR",
38992
+ "MONTH",
38993
+ "DAY",
38994
+ "DATEDIFF",
38995
+ "ABS",
38996
+ "MOD",
38997
+ "POWER",
38998
+ "SQRT"
38999
+ ]);
39000
+ function stringFunctionColumnMeta(expr) {
39001
+ if (expr.func === "CAST") {
39002
+ const target = expr.args[1];
39003
+ return target?.type === "STRING" && target.value === "NUMBER" ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
39004
+ }
39005
+ return NUMBER_RETURNING_STRING_FUNCTIONS.has(expr.func) ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
39006
+ }
39007
+ function caseResultColumnMeta(result, resolveField2) {
39008
+ if (result.type === "STRING") return syntheticColumnMeta("string");
39009
+ if (result.type === "ARRAY") return unsupportedColumnMeta();
39010
+ if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
39011
+ if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
39012
+ const source = resolveField2(aggregateFieldRef(result.field));
39013
+ return source ?? unknownStringColumnMeta();
39014
+ }
39015
+ function mergeExpressionColumnMeta(candidates) {
39016
+ if (candidates.length === 0) return unknownStringColumnMeta();
39017
+ const first = candidates[0];
39018
+ const withoutSource = (semantics) => {
39019
+ if (!semantics) return void 0;
39020
+ const { source: _source, ...rest } = semantics;
39021
+ return rest;
39022
+ };
39023
+ if (candidates.every(
39024
+ (candidate) => candidate.sortKind === first.sortKind && candidate.fieldType === first.fieldType && fieldSemanticsEqual(withoutSource(candidate.semantics), withoutSource(first.semantics))
39025
+ )) {
39026
+ const sameSource = candidates.every(
39027
+ (candidate) => fieldSemanticsEqual(candidate.semantics, first.semantics)
39028
+ );
39029
+ return sameSource ? first : { ...first, semantics: withoutSource(first.semantics) };
39030
+ }
39031
+ if (candidates.some((candidate) => candidate.semantics?.compareMode === "unsupported")) {
39032
+ return unsupportedColumnMeta("KSQL_MIXED_UNSUPPORTED");
39033
+ }
39034
+ return unknownStringColumnMeta();
37967
39035
  }
37968
39036
  function selectNeedsSourceColumnMeta(stmt) {
37969
39037
  return stmt.columns.some(
37970
- (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"
39038
+ (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"
37971
39039
  );
37972
39040
  }
37973
39041
  async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
@@ -37984,18 +39052,18 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
37984
39052
  if (ref.tableAlias !== null) {
37985
39053
  if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
37986
39054
  const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
37987
- return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
39055
+ return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
37988
39056
  }
37989
39057
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
37990
39058
  if (!table) return void 0;
37991
39059
  if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
37992
39060
  const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37993
- return info ? materializedMetaFromFieldInfo(info) : void 0;
39061
+ return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
37994
39062
  }
37995
39063
  if (stmt.joins.length === 0) {
37996
39064
  if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
37997
39065
  const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
37998
- return info ? materializedMetaFromFieldInfo(info) : void 0;
39066
+ return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
37999
39067
  }
38000
39068
  const matches = tables.flatMap((table) => {
38001
39069
  if (table.cteName !== null) {
@@ -38004,7 +39072,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
38004
39072
  return [materialized.columnMeta?.get(ref.field)];
38005
39073
  }
38006
39074
  const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
38007
- return info ? [materializedMetaFromFieldInfo(info)] : [];
39075
+ const meta3 = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
39076
+ return meta3 ? [meta3] : [];
38008
39077
  });
38009
39078
  return matches.length === 1 ? matches[0] : void 0;
38010
39079
  };
@@ -38034,19 +39103,27 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
38034
39103
  meta3 = resolveField2(aggregateFieldRef(column.field));
38035
39104
  } else if (column.type === "AGGREGATE") {
38036
39105
  if (column.func === "GROUP_CONCAT") {
38037
- meta3 = { sortKind: "string" };
39106
+ meta3 = syntheticColumnMeta("string");
38038
39107
  } else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
38039
- meta3 = { sortKind: "number" };
39108
+ meta3 = syntheticColumnMeta("number");
38040
39109
  } else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
38041
39110
  const source = resolveField2(aggregateFieldRef(column.arg.field));
38042
- if (source?.sortKind) meta3 = { sortKind: source.sortKind };
39111
+ if (source) meta3 = source;
38043
39112
  }
38044
39113
  } else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
38045
- meta3 = { sortKind: "number" };
39114
+ meta3 = syntheticColumnMeta("number");
38046
39115
  } else if (column.type === "LITERAL_COL") {
38047
- meta3 = { sortKind: "string" };
39116
+ meta3 = syntheticColumnMeta("string");
39117
+ } else if (column.type === "STRFUNC_COL") {
39118
+ meta3 = stringFunctionColumnMeta(column.expr);
38048
39119
  } else if (column.type === "WINDOW_COL") {
38049
- meta3 = { sortKind: "number" };
39120
+ meta3 = syntheticColumnMeta("number");
39121
+ } else if (column.type === "CASE_COL") {
39122
+ const results = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
39123
+ if (column.expr.elseResult) results.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
39124
+ meta3 = mergeExpressionColumnMeta(results);
39125
+ } else if (column.type === "SCALAR_SUBQUERY_COL") {
39126
+ meta3 = unknownStringColumnMeta();
38050
39127
  }
38051
39128
  if (meta3) inferred.set(output, meta3);
38052
39129
  });
@@ -38060,7 +39137,8 @@ function mergeUnionColumnMeta(left, right) {
38060
39137
  const a = leftMeta?.get(column);
38061
39138
  const rightColumn = right.columns[index];
38062
39139
  const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
38063
- if (a && b && a.sortKind === b.sortKind && a.fieldType === b.fieldType) merged.set(column, a);
39140
+ if (a && b) merged.set(column, mergeExpressionColumnMeta([a, b]));
39141
+ else if (a || b) merged.set(column, unknownStringColumnMeta());
38064
39142
  });
38065
39143
  return merged;
38066
39144
  }
@@ -38097,7 +39175,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
38097
39175
  };
38098
39176
  return { row, having };
38099
39177
  }
38100
- async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
39178
+ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
38101
39179
  const maxRecords2 = options.maxRecords ?? 1e4;
38102
39180
  const warnings = /* @__PURE__ */ new Set();
38103
39181
  const parallel = options.fetchParallel ?? 1;
@@ -38111,6 +39189,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38111
39189
  loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
38112
39190
  ]);
38113
39191
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
39192
+ const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
39193
+ stmt,
39194
+ client,
39195
+ cacheContext,
39196
+ cteCache,
39197
+ whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
39198
+ );
39199
+ const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
38114
39200
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
38115
39201
  validateKlikePushdownPlan(pushdownPlan);
38116
39202
  const mainPushDown = pushdownPlan.mainCondition;
@@ -38124,7 +39210,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38124
39210
  true,
38125
39211
  options.onLimitReached ?? "error",
38126
39212
  warnings,
38127
- mainPushDown
39213
+ mainPushDown,
39214
+ allowOriginalWherePushdown
38128
39215
  );
38129
39216
  const parallelJoins = [];
38130
39217
  const onOptJoins = [];
@@ -38150,7 +39237,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38150
39237
  }
38151
39238
  }
38152
39239
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
38153
- const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
39240
+ const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
38154
39241
  scalarCachePromise.catch(() => {
38155
39242
  });
38156
39243
  orderByMetaPromise.catch(() => {
@@ -38187,15 +39274,18 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
38187
39274
  tables.set(join.table.alias, joinRecords);
38188
39275
  }));
38189
39276
  const scalarCache = await scalarCachePromise;
38190
- const { optionOrders, sortKinds } = await orderByMetaPromise;
39277
+ const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
38191
39278
  const { rows, columns } = runFullScan({
38192
39279
  tables,
38193
39280
  stmt,
38194
39281
  scalarCache,
38195
39282
  optionOrders,
38196
39283
  sortKinds,
39284
+ orderSemantics: semantics,
38197
39285
  fieldTypeResolver: fieldTypeResolvers.row,
39286
+ fieldSemanticsResolver,
38198
39287
  havingFieldTypeResolver: fieldTypeResolvers.having,
39288
+ havingFieldSemanticsResolver,
38199
39289
  aggregateSortKindResolver,
38200
39290
  appliedKlikes: pushdownPlan.appliedKlikes
38201
39291
  });
@@ -38292,16 +39382,39 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38292
39382
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
38293
39383
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
38294
39384
  ]);
39385
+ const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
39386
+ if (whereCapability.capability === "UNSUPPORTED") {
39387
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
39388
+ }
39389
+ const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
39390
+ if (hasCanonicalOrder(stmt)) {
39391
+ (stmt.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
39392
+ stmt,
39393
+ staticMode: "FULL_SCAN",
39394
+ whereCapability: whereCapability.capability,
39395
+ orderSemantics: orderMeta.semantics,
39396
+ maxRecords: maxRecords2,
39397
+ hasKlike: whereHasKlike(stmt.where)
39398
+ });
39399
+ }
38295
39400
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
38296
39401
  loadTypedPushdownMeta(stmt, client, cacheContext),
38297
39402
  loadTypedInFieldTypes(stmt, client, cacheContext),
38298
39403
  loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
38299
39404
  ]);
38300
39405
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
39406
+ const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
39407
+ stmt,
39408
+ client,
39409
+ cacheContext,
39410
+ cteCache,
39411
+ whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
39412
+ );
39413
+ const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
38301
39414
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
38302
39415
  validateKlikePushdownPlan(pushdownPlan);
38303
39416
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
38304
- const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
39417
+ const orderByMetaPromise = Promise.resolve(orderMeta);
38305
39418
  scalarCachePromise.catch(() => {
38306
39419
  });
38307
39420
  orderByMetaPromise.catch(() => {
@@ -38320,7 +39433,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38320
39433
  true,
38321
39434
  options.onLimitReached ?? "error",
38322
39435
  warnings,
38323
- pushdownPlan.mainCondition
39436
+ pushdownPlan.mainCondition,
39437
+ whereCapability.capability === "EXACT_PUSHDOWN"
38324
39438
  );
38325
39439
  tables.set(stmt.from.alias, mainRecords);
38326
39440
  }
@@ -38357,7 +39471,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38357
39471
  });
38358
39472
  await Promise.all(joinFetches);
38359
39473
  const scalarCache = await scalarCachePromise;
38360
- const { optionOrders, sortKinds } = await orderByMetaPromise;
39474
+ const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
38361
39475
  const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
38362
39476
  const { rows, columns } = runFullScan({
38363
39477
  tables,
@@ -38365,8 +39479,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
38365
39479
  scalarCache,
38366
39480
  optionOrders,
38367
39481
  sortKinds,
39482
+ orderSemantics: semantics,
38368
39483
  fieldTypeResolver: fieldTypeResolvers.row,
39484
+ fieldSemanticsResolver,
38369
39485
  havingFieldTypeResolver: fieldTypeResolvers.having,
39486
+ havingFieldSemanticsResolver,
38370
39487
  aggregateSortKindResolver,
38371
39488
  appliedKlikes: pushdownPlan.appliedKlikes,
38372
39489
  sourceColumns
@@ -38378,13 +39495,13 @@ function processRowToKintoneRecord(row) {
38378
39495
  Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
38379
39496
  );
38380
39497
  }
38381
- async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords2, parallel, isMainTable, onLimit2, warnings, pushDownCond = null) {
39498
+ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords2, parallel, isMainTable, onLimit2, warnings, pushDownCond = null, allowOriginalWherePushdown = true) {
38382
39499
  const fields = selectToFetchAllFields(stmt, table);
38383
39500
  const onTruncate = (max) => {
38384
39501
  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`);
38385
39502
  };
38386
39503
  if (!table.subtableCode) {
38387
- const baseQuery = isMainTable ? selectToFetchAllParams(stmt, table.appId).query : "";
39504
+ const baseQuery = isMainTable && allowOriginalWherePushdown ? selectToFetchAllParams(stmt, table.appId).query : "";
38388
39505
  const pushQuery = pushDownCond !== null ? whereToKintone(pushDownCond) : "";
38389
39506
  const query = baseQuery && pushQuery ? `(${baseQuery}) and (${pushQuery})` : baseQuery || pushQuery;
38390
39507
  const resolved = await fetchRecordsForSharedPlan(client.getRecords, table.appId, query, fields, {
@@ -38588,6 +39705,10 @@ async function getProcessStatusesCached(appId, client, cacheContext) {
38588
39705
  setScopedCacheValue(processStatusCache, cacheContext, appId, loading);
38589
39706
  return loading;
38590
39707
  }
39708
+ async function loadProcessStatusOrder(appId, client, cacheContext) {
39709
+ const process4 = await getProcessStatusesCached(appId, client, cacheContext);
39710
+ return process4.enable && process4.states !== null ? new Map(process4.states.map((state) => [state.name, state.index])) : void 0;
39711
+ }
38591
39712
  async function getFieldTypeMap(appId, client, cacheContext) {
38592
39713
  const cached2 = getScopedCacheValue(fieldTypeCache, cacheContext, appId);
38593
39714
  if (cached2) return cached2;
@@ -38631,18 +39752,118 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
38631
39752
  setScopedCacheValue(sortKindCache, cacheContext, appId, map2);
38632
39753
  return map2;
38633
39754
  }
38634
- async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
39755
+ function orderByFieldNames(stmt) {
39756
+ const items = [
39757
+ ...stmt.orderBy,
39758
+ ...stmt.columns.flatMap((column) => column.type === "WINDOW_COL" ? column.orderBy : [])
39759
+ ];
39760
+ return [...new Set(items.flatMap(
39761
+ (item) => item.key.type === "FIELD_NAME" ? [item.key.name] : []
39762
+ ))];
39763
+ }
39764
+ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables) {
39765
+ const names = orderByFieldNames(stmt);
39766
+ if (names.length === 0) return /* @__PURE__ */ new Map();
39767
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
39768
+ const ambiguousFields = /* @__PURE__ */ new Set();
39769
+ const infosByApp = new Map(
39770
+ await Promise.all([...new Set(
39771
+ tables.filter((table) => table.cteName === null).map((table) => table.appId)
39772
+ )].map(async (appId) => {
39773
+ const infos = await getFieldsCached(appId, client, cacheContext);
39774
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
39775
+ }))
39776
+ );
39777
+ const resolveField2 = (ref) => {
39778
+ if (ref.tableAlias !== null) {
39779
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
39780
+ const info2 = infosByApp.get(stmt.from.appId)?.get(ref.field);
39781
+ return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
39782
+ }
39783
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
39784
+ if (!table) return void 0;
39785
+ if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
39786
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
39787
+ return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
39788
+ }
39789
+ if (stmt.joins.length === 0) {
39790
+ if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
39791
+ const info = infosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
39792
+ return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
39793
+ }
39794
+ const matches = tables.flatMap((table) => {
39795
+ if (table.cteName !== null) {
39796
+ const materialized = materializedTables?.get(table.cteName);
39797
+ const meta4 = materialized?.columns.includes(ref.field) ? materialized.columnMeta?.get(ref.field) : void 0;
39798
+ return meta4 ? [meta4] : [];
39799
+ }
39800
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
39801
+ const meta3 = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
39802
+ return meta3 ? [meta3] : [];
39803
+ });
39804
+ if (matches.length > 1) ambiguousFields.add(ref.field);
39805
+ return matches.length === 1 ? matches[0] : void 0;
39806
+ };
39807
+ const aliasSemantics = /* @__PURE__ */ new Map();
39808
+ for (const column of stmt.columns) {
39809
+ if (!("alias" in column) || !column.alias) continue;
39810
+ let meta3;
39811
+ if (column.type === "FIELD") meta3 = resolveField2(aggregateFieldRef(column.field));
39812
+ else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
39813
+ meta3 = syntheticColumnMeta("number");
39814
+ } else if (column.type === "LITERAL_COL") meta3 = syntheticColumnMeta("string");
39815
+ else if (column.type === "STRFUNC_COL") meta3 = stringFunctionColumnMeta(column.expr);
39816
+ else if (column.type === "SCALAR_SUBQUERY_COL") meta3 = unknownStringColumnMeta();
39817
+ else if (column.type === "CASE_COL") {
39818
+ const candidates = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
39819
+ if (column.expr.elseResult) candidates.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
39820
+ meta3 = mergeExpressionColumnMeta(candidates);
39821
+ } else if (column.type === "AGGREGATE") {
39822
+ if (column.func === "MIN" || column.func === "MAX") {
39823
+ meta3 = column.arg.type === "FIELD_REF" ? resolveField2(aggregateFieldRef(column.arg.field)) : syntheticColumnMeta("number");
39824
+ } else {
39825
+ meta3 = column.func === "GROUP_CONCAT" ? syntheticColumnMeta("string") : syntheticColumnMeta("number");
39826
+ }
39827
+ }
39828
+ if (meta3?.semantics) aliasSemantics.set(column.alias, meta3.semantics);
39829
+ }
39830
+ const result = /* @__PURE__ */ new Map();
39831
+ for (const name of names) {
39832
+ const base = aliasSemantics.get(name) ?? resolveField2(aggregateFieldRef(name))?.semantics;
39833
+ if (!base) {
39834
+ const ref = aggregateFieldRef(name);
39835
+ if (ref.tableAlias === null && ambiguousFields.has(ref.field)) {
39836
+ result.set(name, resolveFieldSemantics({ fieldType: "KSQL_AMBIGUOUS" }));
39837
+ }
39838
+ continue;
39839
+ }
39840
+ let semantics = base;
39841
+ if (base.fieldType === "STATUS" && base.source && stmt.orderMode !== "KINTONE_NATIVE") {
39842
+ const process4 = await getProcessStatusesCached(base.source.appId, client, cacheContext);
39843
+ if (process4.enable && process4.states !== null) {
39844
+ semantics = {
39845
+ ...base,
39846
+ optionOrder: new Map(process4.states.map((state) => [state.name, state.index]))
39847
+ };
39848
+ }
39849
+ }
39850
+ result.set(name, semantics);
39851
+ }
39852
+ return result;
39853
+ }
39854
+ async function buildOrderByMetaForSelect(stmt, client, cacheContext, materializedTables) {
38635
39855
  const hasWindowOrderBy = stmt.columns.some(
38636
39856
  (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
38637
39857
  );
38638
39858
  if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
38639
- return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
39859
+ return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map(), semantics: /* @__PURE__ */ new Map() };
38640
39860
  }
38641
- const [optionOrders, sortKinds] = await Promise.all([
39861
+ const [optionOrders, sortKinds, semantics] = await Promise.all([
38642
39862
  buildOptionOrdersForSelect(stmt, client, cacheContext),
38643
- buildSortKindsForSelect(stmt, client, cacheContext)
39863
+ buildSortKindsForSelect(stmt, client, cacheContext),
39864
+ buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables)
38644
39865
  ]);
38645
- return { optionOrders, sortKinds };
39866
+ return { optionOrders, sortKinds, semantics };
38646
39867
  }
38647
39868
  async function buildOptionOrdersForSelect(stmt, client, cacheContext) {
38648
39869
  const optionOrders = /* @__PURE__ */ new Map();
@@ -38740,6 +39961,9 @@ var RejectLimitExceededError = class extends Error {
38740
39961
  }
38741
39962
  };
38742
39963
  async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
39964
+ if (stmt.type === "UPDATE") {
39965
+ await assertDmlWhereCapability(stmt, client, cacheContext);
39966
+ }
38743
39967
  const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
38744
39968
  const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
38745
39969
  if (new Set(payloadFields).size !== payloadFields.length) {
@@ -38779,18 +40003,22 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
38779
40003
  const columnMeta = /* @__PURE__ */ new Map();
38780
40004
  for (const column of payloadFields) {
38781
40005
  if (column === "$id") {
38782
- columnMeta.set(column, { sortKind: "number", fieldType: "RECORD_NUMBER" });
40006
+ columnMeta.set(column, {
40007
+ sortKind: "number",
40008
+ fieldType: "RECORD_NUMBER",
40009
+ semantics: resolveFieldSemantics({ fieldType: "RECORD_NUMBER" })
40010
+ });
38783
40011
  continue;
38784
40012
  }
38785
40013
  const info = infoByCode.get(column);
38786
- if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
38787
- }
38788
- columnMeta.set("$err_statement", { sortKind: "number" });
38789
- columnMeta.set("$err_operation", { sortKind: "string" });
38790
- columnMeta.set("$err_row", { sortKind: "number" });
38791
- columnMeta.set("$err_field", { sortKind: "string" });
38792
- columnMeta.set("$err_code", { sortKind: "string" });
38793
- columnMeta.set("$err_message", { sortKind: "string" });
40014
+ if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info, stmt.appId));
40015
+ }
40016
+ columnMeta.set("$err_statement", syntheticColumnMeta("number"));
40017
+ columnMeta.set("$err_operation", syntheticColumnMeta("string"));
40018
+ columnMeta.set("$err_row", syntheticColumnMeta("number"));
40019
+ columnMeta.set("$err_field", syntheticColumnMeta("string"));
40020
+ columnMeta.set("$err_code", syntheticColumnMeta("string"));
40021
+ columnMeta.set("$err_message", syntheticColumnMeta("string"));
38794
40022
  materializedMetaByValidationResult.set(result, columnMeta);
38795
40023
  return { result, candidates, invalidRowNumbers, columnMeta };
38796
40024
  }
@@ -39176,6 +40404,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
39176
40404
  };
39177
40405
  }
39178
40406
  async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
40407
+ await assertDmlWhereCapability(stmt, client, cacheContext);
39179
40408
  if (stmt.subtableCode) {
39180
40409
  return executeUpdateSubtable(stmt, client, options, cacheContext);
39181
40410
  }
@@ -39253,6 +40482,7 @@ function collectUpdateFromTargetFields(stmt) {
39253
40482
  return [...fields];
39254
40483
  }
39255
40484
  async function executeDelete(stmt, client, options, cacheContext) {
40485
+ await assertDmlWhereCapability(stmt, client, cacheContext);
39256
40486
  if (stmt.subtableCode) {
39257
40487
  return executeDeleteSubtable(stmt, client, options, cacheContext);
39258
40488
  }
@@ -39625,6 +40855,18 @@ async function executeReorder(stmt, client, options, cacheContext) {
39625
40855
  client,
39626
40856
  cacheContext
39627
40857
  );
40858
+ const reorderFields = await getFieldsCached(stmt.appId, client, cacheContext);
40859
+ const reorderSemanticsByCode = new Map(reorderFields.map((field) => [
40860
+ field.code,
40861
+ field.semantics ?? resolveFieldSemantics(field)
40862
+ ]));
40863
+ const resolveReorderSemantics = (field) => {
40864
+ if (field.field === "_idx" || field.field === "_pid" || field.field === "_rid") {
40865
+ return syntheticSemantics("number");
40866
+ }
40867
+ const code = field.field.startsWith("_p.") ? field.field.slice(3) : field.field;
40868
+ return reorderSemanticsByCode.get(code) ?? syntheticSemantics("string");
40869
+ };
39628
40870
  const parents = await fetchAll(
39629
40871
  client.getRecords,
39630
40872
  stmt.appId,
@@ -39633,7 +40875,13 @@ async function executeReorder(stmt, client, options, cacheContext) {
39633
40875
  { maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
39634
40876
  );
39635
40877
  const expanded = expandRowsForSubtableDml(parents, stmt.subtableCode);
39636
- 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));
40878
+ const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(
40879
+ stmt.where,
40880
+ r.flat,
40881
+ resolveFieldType,
40882
+ void 0,
40883
+ resolveReorderSemantics
40884
+ )).map((r) => r.parentId));
39637
40885
  if (options.confirm) {
39638
40886
  const ok = await options.confirm(targetParentIds.size, "UPDATE");
39639
40887
  if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
@@ -39644,7 +40892,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
39644
40892
  if (!parent) continue;
39645
40893
  const rows = getMutableTableRows(parent, stmt.subtableCode);
39646
40894
  const sortable = rows.map((row, i) => ({ row, i, flat: buildFlatRowForSort(parent, stmt.subtableCode, row, i) }));
39647
- sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by));
40895
+ sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by, resolveReorderSemantics));
39648
40896
  const orderedRowIds = sortable.map((x) => x.row.id ?? "");
39649
40897
  await client.putRecords(buildSubtableReorderPutParams(stmt.appId, pid, getRevision(parent), stmt.subtableCode, orderedRowIds));
39650
40898
  }
@@ -39665,14 +40913,12 @@ function buildFlatRowForSort(parent, subtableCode, row, idx) {
39665
40913
  }
39666
40914
  return flat;
39667
40915
  }
39668
- function compareByOrder(a, b, orderBy) {
40916
+ function compareByOrder(a, b, orderBy, resolveSemantics) {
39669
40917
  for (const item of orderBy) {
39670
40918
  const av = evalOrderKeyForRow(item.key, a);
39671
40919
  const bv = evalOrderKeyForRow(item.key, b);
39672
- const an = Number(av);
39673
- const bn = Number(bv);
39674
- const numeric = !Number.isNaN(an) && !Number.isNaN(bn);
39675
- const cmp = numeric ? an - bn : av.localeCompare(bv, "ja");
40920
+ 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");
40921
+ const cmp = compareCanonicalValues(av, bv, semantics ?? syntheticSemantics("string"));
39676
40922
  if (cmp !== 0) return item.direction === "ASC" ? cmp : -cmp;
39677
40923
  }
39678
40924
  return 0;
@@ -39869,35 +41115,140 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
39869
41115
  pending.forEach(([i], idx) => cache.set(i, values[idx]));
39870
41116
  return cache;
39871
41117
  }
39872
- function buildBatchExplainPlans(sql, injectedVariables) {
41118
+ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords2 = 1e4) {
41119
+ const fieldApps = /* @__PURE__ */ new Set();
41120
+ const processStatusApps = /* @__PURE__ */ new Set();
41121
+ const tracedClient = {
41122
+ ...client,
41123
+ getFields: async (appId) => {
41124
+ fieldApps.add(appId);
41125
+ return client.getFields(appId);
41126
+ },
41127
+ getProcessStatuses: async (appId) => {
41128
+ processStatusApps.add(appId);
41129
+ return client.getProcessStatuses(appId);
41130
+ }
41131
+ };
41132
+ const capabilities = /* @__PURE__ */ new Map();
41133
+ const orderPlans = /* @__PURE__ */ new Map();
41134
+ const seen = /* @__PURE__ */ new Set();
41135
+ const visit = async (node) => {
41136
+ if (node === null || typeof node !== "object") return;
41137
+ if (seen.has(node)) return;
41138
+ seen.add(node);
41139
+ if (Array.isArray(node)) {
41140
+ await Promise.all(node.map(visit));
41141
+ return;
41142
+ }
41143
+ const typed = node;
41144
+ if (typed["type"] === "SELECT") {
41145
+ const select = node;
41146
+ const physicalApps = [select.from, ...select.joins.map((join) => join.table)].filter((table) => table.cteName === null).map((table) => table.appId);
41147
+ const needsWhereSchema = whereNeedsFieldMetadata(select.where);
41148
+ if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
41149
+ physicalApps.forEach((appId) => fieldApps.add(appId));
41150
+ }
41151
+ const capability = await resolveSelectWhereCapability(select, tracedClient, cacheContext);
41152
+ if (capability.capability === "UNSUPPORTED") {
41153
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
41154
+ }
41155
+ capabilities.set(select, capability);
41156
+ if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
41157
+ const meta3 = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
41158
+ if (select.orderMode !== "KINTONE_NATIVE") {
41159
+ for (const semantics of meta3.semantics.values()) {
41160
+ if (semantics.fieldType === "STATUS" && semantics.source) {
41161
+ processStatusApps.add(semantics.source.appId);
41162
+ }
41163
+ }
41164
+ }
41165
+ const hasUnmaterializedSource = [select.from, ...select.joins.map((join) => join.table)].some((table) => table.cteName !== null);
41166
+ if (hasCanonicalOrder(select) && !hasUnmaterializedSource) {
41167
+ const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
41168
+ orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
41169
+ stmt: select,
41170
+ staticMode: mode,
41171
+ whereCapability: capability.capability,
41172
+ orderSemantics: meta3.semantics,
41173
+ maxRecords: maxRecords2,
41174
+ hasKlike: whereHasKlike(select.where)
41175
+ }));
41176
+ }
41177
+ }
41178
+ } else if (typed["type"] === "UPDATE" || typed["type"] === "DELETE") {
41179
+ fieldApps.add(node.appId);
41180
+ await assertDmlWhereCapability(
41181
+ node,
41182
+ tracedClient,
41183
+ cacheContext
41184
+ );
41185
+ }
41186
+ await Promise.all(Object.values(typed).map(visit));
41187
+ };
41188
+ await visit(query);
41189
+ if (typeof query === "object" && query !== null && query.type === "WITH" && canInlineSingleCte(query)) {
41190
+ const inlined = buildInlinedQuery(query);
41191
+ const capability = await resolveSelectWhereCapability(inlined, tracedClient, cacheContext);
41192
+ if (capability.capability === "UNSUPPORTED") {
41193
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
41194
+ }
41195
+ capabilities.set(inlined, capability);
41196
+ if (hasCanonicalOrder(inlined)) {
41197
+ const meta3 = await buildOrderByMetaForSelect(inlined, tracedClient, cacheContext);
41198
+ orderPlans.set(inlined, (inlined.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
41199
+ stmt: inlined,
41200
+ staticMode: capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(inlined) : "FULL_SCAN",
41201
+ whereCapability: capability.capability,
41202
+ orderSemantics: meta3.semantics,
41203
+ maxRecords: maxRecords2,
41204
+ hasKlike: whereHasKlike(inlined.where)
41205
+ }));
41206
+ }
41207
+ }
41208
+ return { capabilities, orderPlans, fieldApps, processStatusApps };
41209
+ }
41210
+ function explainMetadataLines(analysis) {
41211
+ return [
41212
+ ...[...analysis.fieldApps].sort((a, b) => a - b).map((appId) => ` metadata API: form definition APP${appId}`),
41213
+ ...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`)
41214
+ ];
41215
+ }
41216
+ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords2 = 1e4) {
39873
41217
  const statements = parseSqlBatch(sql);
39874
41218
  const analysis = analyzeBatch(statements);
39875
41219
  validateDeclaredBatchVariables(statements, injectedVariables);
39876
41220
  const variables = /* @__PURE__ */ new Map();
39877
- return {
39878
- statementCount: statements.length,
39879
- statements: statements.map((stmt, i) => {
39880
- const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
39881
- validateKlikeStatement(planStmt);
39882
- const result = {
39883
- index: i,
39884
- type: analysis.statements[i].statementType,
39885
- plan: buildBatchStatementPlan(planStmt, analysis.statements[i])
39886
- };
39887
- if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
39888
- variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
39889
- }
39890
- return result;
39891
- })
39892
- };
41221
+ const plans = [];
41222
+ for (let i = 0; i < statements.length; i++) {
41223
+ const stmt = statements[i];
41224
+ const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
41225
+ validateKlikeStatement(planStmt);
41226
+ const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords2);
41227
+ const statementPlan = buildBatchStatementPlan(
41228
+ planStmt,
41229
+ analysis.statements[i],
41230
+ whereAnalysis.capabilities,
41231
+ whereAnalysis.orderPlans
41232
+ );
41233
+ const metadataPlan = explainMetadataLines(whereAnalysis);
41234
+ plans.push({
41235
+ index: i,
41236
+ type: analysis.statements[i].statementType,
41237
+ plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
41238
+ });
41239
+ if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
41240
+ variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
41241
+ }
41242
+ }
41243
+ return { statementCount: statements.length, statements: plans };
39893
41244
  }
39894
- function buildBatchStatementPlan(stmt, info) {
41245
+ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans) {
39895
41246
  if (stmt.type === "CREATE_TEMP_TABLE") {
39896
41247
  return [
39897
41248
  `CREATE TEMP TABLE ${stmt.name}`,
39898
41249
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
39899
41250
  ` 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`,
39900
- ...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
41251
+ ...buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans).map((l) => ` ${l}`)
39901
41252
  ];
39902
41253
  }
39903
41254
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -39913,7 +41264,7 @@ function buildBatchStatementPlan(stmt, info) {
39913
41264
  `SET @${stmt.name} = (SELECT ...)`,
39914
41265
  " 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",
39915
41266
  " subquery:",
39916
- ...buildPlanForBatchQuery(stmt.expr.query, subInfo).map((l) => ` ${l}`)
41267
+ ...buildPlanForBatchQuery(stmt.expr.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`)
39917
41268
  ];
39918
41269
  }
39919
41270
  return [
@@ -39929,7 +41280,7 @@ function buildBatchStatementPlan(stmt, info) {
39929
41280
  }
39930
41281
  if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
39931
41282
  if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
39932
- if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
41283
+ if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans);
39933
41284
  if (stmt.type === "ASSERT") {
39934
41285
  const lines = [
39935
41286
  `ASSERT ${stmt.text}`,
@@ -39941,11 +41292,11 @@ function buildBatchStatementPlan(stmt, info) {
39941
41292
  subqueries.forEach((sq, i) => {
39942
41293
  lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
39943
41294
  const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
39944
- lines.push(...buildPlanForBatchQuery(sq.query, subInfo).map((l) => ` ${l}`));
41295
+ lines.push(...buildPlanForBatchQuery(sq.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`));
39945
41296
  });
39946
41297
  return lines;
39947
41298
  }
39948
- return buildPlanForBatchQuery(stmt, info);
41299
+ return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
39949
41300
  }
39950
41301
  function hasTempTableRef(node) {
39951
41302
  if (Array.isArray(node)) return node.some(hasTempTableRef);
@@ -39957,9 +41308,9 @@ function hasTempTableRef(node) {
39957
41308
  }
39958
41309
  return false;
39959
41310
  }
39960
- function buildPlanForBatchQuery(query, info) {
41311
+ function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
39961
41312
  if (info.tempTablesReferenced.length === 0) {
39962
- return buildExplainPlan(query);
41313
+ return buildExplainPlan(query, void 0, capabilities, orderPlans);
39963
41314
  }
39964
41315
  const lines = [];
39965
41316
  if (query.type === "INSERT_SELECT") {
@@ -39984,8 +41335,12 @@ function buildPlanForBatchQuery(query, info) {
39984
41335
  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");
39985
41336
  return lines;
39986
41337
  }
39987
- function executeExplain(stmt) {
39988
- const lines = buildExplainPlan(stmt.query);
41338
+ async function executeExplain(stmt, client, cacheContext, maxRecords2) {
41339
+ const analysis = await buildExplainWhereAnalysis(stmt.query, client, cacheContext, maxRecords2);
41340
+ const lines = [
41341
+ ...explainMetadataLines(analysis),
41342
+ ...buildExplainPlan(stmt.query, void 0, analysis.capabilities, analysis.orderPlans)
41343
+ ];
39989
41344
  return {
39990
41345
  type: "SELECT",
39991
41346
  columns: ["plan"],
@@ -39993,29 +41348,45 @@ function executeExplain(stmt) {
39993
41348
  rowCount: lines.length
39994
41349
  };
39995
41350
  }
39996
- function buildExplainPlan(query, label) {
39997
- if (query.type === "UNION") return buildUnionPlan(query);
39998
- if (query.type === "WITH") return buildWithPlan(query);
41351
+ function buildExplainPlan(query, label, capabilities, orderPlans) {
41352
+ if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
41353
+ if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
39999
41354
  if (query.type === "INSERT") return buildInsertPlan(query, label);
40000
- if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label);
41355
+ if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
40001
41356
  if (query.type === "UPSERT") return buildUpsertPlan(query, label);
40002
- if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label);
40003
- if (query.type === "UPDATE") return buildUpdatePlan(query, label);
41357
+ if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans);
41358
+ if (query.type === "UPDATE") return buildUpdatePlan(query, label, capabilities, orderPlans);
40004
41359
  if (query.type === "DELETE") return buildDeletePlan(query, label);
40005
41360
  if (query.type === "REORDER") return buildReorderPlan(query, label);
40006
- return buildSelectPlan(query, label);
41361
+ return buildSelectPlan(query, label, capabilities, orderPlans);
40007
41362
  }
40008
- function buildSelectPlan(stmt, label) {
40009
- const mode = resolveSelectMode(stmt);
41363
+ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
41364
+ const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
41365
+ const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
41366
+ const mode = orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN" ? "FULL_SCAN" : resolveSelectMode(stmt);
40010
41367
  const reasons = collectFullScanReasons(stmt);
41368
+ if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
41369
+ reasons.push(...whereCapability.reasons.map((reason) => reason.code));
41370
+ }
40011
41371
  const lines = [];
40012
41372
  if (label) lines.push(label);
40013
41373
  lines.push(` mode: ${mode}`);
41374
+ if (orderPlan) {
41375
+ lines.push(` order plan: ${orderPlan.kind}`);
41376
+ if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
41377
+ if (orderPlan.kind === "KORDER_NATIVE") {
41378
+ lines.push(" order semantics: kintone native (not kSQL canonical)");
41379
+ lines.push(" REST execution: single GET");
41380
+ }
41381
+ }
41382
+ if (orderPlan?.requiresCompleteInput ?? requiresCompleteInput(stmt)) {
41383
+ lines.push(" complete input: required (ORDER BY / window ORDER BY; onLimit=truncate disabled)");
41384
+ }
40014
41385
  if (mode === "FULL_SCAN" && reasons.length > 0) {
40015
41386
  lines.push(` reason: ${reasons.join(", ")}`);
40016
41387
  }
40017
41388
  if (mode === "SIMPLE") {
40018
- const params = selectToKintoneParams(stmt);
41389
+ const params = selectToKintoneParams(orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt);
40019
41390
  lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
40020
41391
  lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
40021
41392
  lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
@@ -40025,7 +41396,8 @@ function buildSelectPlan(stmt, label) {
40025
41396
  const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
40026
41397
  const mainPushDown = pushdownPlan.mainCondition;
40027
41398
  const mainCandidate = extractMainTypedPushdownCandidate(stmt);
40028
- const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
41399
+ const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
41400
+ const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)";
40029
41401
  lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
40030
41402
  lines.push(` kintone query: ${mainQ}`);
40031
41403
  if (mainCandidate !== null) {
@@ -40047,10 +41419,10 @@ function buildSelectPlan(stmt, label) {
40047
41419
  lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
40048
41420
  }
40049
41421
  }
40050
- lines.push(...collectSubqueryPlans(stmt));
41422
+ lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
40051
41423
  return lines;
40052
41424
  }
40053
- function buildUnionPlan(stmt) {
41425
+ function buildUnionPlan(stmt, capabilities, orderPlans) {
40054
41426
  const selects = [];
40055
41427
  const collect = (u) => {
40056
41428
  if (u.type === "SELECT") {
@@ -40064,24 +41436,25 @@ function buildUnionPlan(stmt) {
40064
41436
  const lines = [];
40065
41437
  selects.forEach((sel, i) => {
40066
41438
  if (i > 0) lines.push("");
40067
- lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`));
41439
+ lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
40068
41440
  });
40069
41441
  return lines;
40070
41442
  }
40071
- function buildWithPlan(stmt) {
41443
+ function buildWithPlan(stmt, capabilities, orderPlans) {
40072
41444
  const lines = [];
40073
41445
  for (const cte of stmt.ctes) {
40074
41446
  if (cte.query.type === "SELECT") {
40075
- lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`));
41447
+ lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
40076
41448
  lines.push("");
40077
41449
  }
40078
41450
  }
40079
41451
  if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
40080
- lines.push(...buildExplainPlan(stmt.query, "[main]"));
41452
+ lines.push(...buildExplainPlan(stmt.query, "[main]", capabilities, orderPlans));
40081
41453
  }
40082
41454
  if (canInlineSingleCte(stmt)) {
40083
41455
  lines.push("");
40084
- lines.push(...buildSelectPlan(buildInlinedQuery(stmt), "[effective: inlined CTE]"));
41456
+ const inlined = buildInlinedQuery(stmt);
41457
+ lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
40085
41458
  }
40086
41459
  return lines;
40087
41460
  }
@@ -40109,7 +41482,7 @@ function collectFullScanReasons(stmt) {
40109
41482
  r.push("ORDER BY \u306B\u5F0F");
40110
41483
  return r;
40111
41484
  }
40112
- function collectSubqueryPlans(stmt) {
41485
+ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
40113
41486
  const lines = [];
40114
41487
  let idx = 1;
40115
41488
  const visitWhere = (w) => {
@@ -40118,16 +41491,16 @@ function collectSubqueryPlans(stmt) {
40118
41491
  case "BINARY":
40119
41492
  if (w.right.type === "SCALAR_SUBQUERY") {
40120
41493
  lines.push("");
40121
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`));
41494
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40122
41495
  }
40123
41496
  if (w.right.type === "SUBQUERY_IN_LIST") {
40124
41497
  lines.push("");
40125
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`));
41498
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40126
41499
  }
40127
41500
  break;
40128
41501
  case "EXISTS":
40129
41502
  lines.push("");
40130
- lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`));
41503
+ lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40131
41504
  break;
40132
41505
  case "LOGICAL":
40133
41506
  visitWhere(w.left);
@@ -40145,7 +41518,7 @@ function collectSubqueryPlans(stmt) {
40145
41518
  for (const col of stmt.columns) {
40146
41519
  if (col.type === "SCALAR_SUBQUERY_COL") {
40147
41520
  lines.push("");
40148
- lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`));
41521
+ lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
40149
41522
  }
40150
41523
  }
40151
41524
  if (stmt.having) visitWhere(stmt.having);
@@ -40163,7 +41536,7 @@ function buildInsertPlan(stmt, label) {
40163
41536
  lines.push(` fields: ${stmt.fields.join(", ")}`);
40164
41537
  return lines;
40165
41538
  }
40166
- function buildInsertSelectPlan(stmt, label) {
41539
+ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
40167
41540
  const lines = [];
40168
41541
  if (label) lines.push(label);
40169
41542
  lines.push(` [INSERT SELECT]`);
@@ -40171,10 +41544,10 @@ function buildInsertSelectPlan(stmt, label) {
40171
41544
  lines.push(` fields: ${stmt.fields.join(", ")}`);
40172
41545
  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`);
40173
41546
  lines.push("");
40174
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
41547
+ lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
40175
41548
  return lines;
40176
41549
  }
40177
- function buildUpdatePlan(stmt, label) {
41550
+ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
40178
41551
  const isArith = hasArithAssignment(stmt);
40179
41552
  const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
40180
41553
  const lines = [];
@@ -40208,7 +41581,7 @@ function buildUpdatePlan(stmt, label) {
40208
41581
  for (const a of stmt.assignments) {
40209
41582
  if (a.value.type === "SCALAR_SUBQUERY") {
40210
41583
  lines.push("");
40211
- lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`));
41584
+ lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans));
40212
41585
  }
40213
41586
  }
40214
41587
  return lines;
@@ -40235,7 +41608,7 @@ function buildUpsertPlan(stmt, label) {
40235
41608
  ` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json \xD7 ${batchCount}`
40236
41609
  ];
40237
41610
  }
40238
- function buildUpsertSelectPlan(stmt, label) {
41611
+ function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
40239
41612
  const lines = [
40240
41613
  ...label ? [label] : [],
40241
41614
  ` [UPSERT SELECT]`,
@@ -40245,7 +41618,7 @@ function buildUpsertSelectPlan(stmt, label) {
40245
41618
  ` 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`,
40246
41619
  ``
40247
41620
  ];
40248
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
41621
+ lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
40249
41622
  return lines;
40250
41623
  }
40251
41624
  function buildReorderPlan(stmt, label) {
@@ -40780,12 +42153,14 @@ function flattenFormFieldProperties(properties) {
40780
42153
  function flattenFields(properties, lookupCopyFields, inSubtable = false) {
40781
42154
  const out = [];
40782
42155
  for (const field of Object.values(properties)) {
40783
- out.push({
42156
+ const optionOrder = toOptionOrderMap(field.options);
42157
+ const sortKind = detectSortKind(field.type, field.format);
42158
+ const info = {
40784
42159
  code: field.code,
40785
42160
  label: field.label,
40786
42161
  fieldType: field.type,
40787
- optionOrder: toOptionOrderMap(field.options),
40788
- sortKind: detectSortKind(field.type, field.format),
42162
+ optionOrder,
42163
+ sortKind,
40789
42164
  required: field.required,
40790
42165
  minValue: normalizeConstraintValue(field.minValue),
40791
42166
  maxValue: normalizeConstraintValue(field.maxValue),
@@ -40794,7 +42169,9 @@ function flattenFields(properties, lookupCopyFields, inSubtable = false) {
40794
42169
  defaultValue: field.defaultValue,
40795
42170
  inSubtable,
40796
42171
  writable: !lookupCopyFields.has(field.code) && !NON_WRITABLE_FIELD_TYPES2.has(field.type)
40797
- });
42172
+ };
42173
+ info.semantics = resolveFieldSemantics(info);
42174
+ out.push(info);
40798
42175
  if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true));
40799
42176
  }
40800
42177
  return out;
@@ -40849,6 +42226,18 @@ function detectSortKind(fieldType, calcFormat) {
40849
42226
  return void 0;
40850
42227
  }
40851
42228
 
42229
+ // src/core/processStatus.ts
42230
+ function normalizeProcessStatusStates(states) {
42231
+ if (states === null) return null;
42232
+ return Object.values(states).map((state) => {
42233
+ const index = Number(state.index);
42234
+ if (!Number.isSafeInteger(index) || index < 0) {
42235
+ throw new Error(`ArgumentError: invalid process status index: ${String(state.index)}`);
42236
+ }
42237
+ return { name: state.name, index };
42238
+ });
42239
+ }
42240
+
40852
42241
  // src/cli/nodeKintoneClient.ts
40853
42242
  var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
40854
42243
  function createNodeKintoneClient(baseUrl, tokenResolver) {
@@ -41046,7 +42435,7 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
41046
42435
  const res = await requestJson(`${apiBasePath}/app/status.json?${qs.toString()}`, { method: "GET" }, appId);
41047
42436
  return {
41048
42437
  enable: res.enable,
41049
- states: Object.values(res.states ?? {}).map((state) => state.name)
42438
+ states: normalizeProcessStatusStates(res.states)
41050
42439
  };
41051
42440
  }
41052
42441
  };
@@ -42053,8 +43442,23 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42053
43442
  } catch (err) {
42054
43443
  throw restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
42055
43444
  }
43445
+ const needsAppMetadata = normalized.appBindingByMappedApp.size > 0 && statements.some(explainNeedsAppMetadata);
43446
+ const runtime = needsAppMetadata ? await createRuntime(serverOptions, {
43447
+ sql: input.sql,
43448
+ sqlContext: normalized.sqlContext,
43449
+ profile: input.profile
43450
+ }) : null;
43451
+ const explainClient = runtime?.client ?? noOpClient();
43452
+ const explainCacheContext = runtime?.cacheContext ?? normalized.cacheContext;
43453
+ const explainSourceSql = runtime?.sql ?? normalized.normalizedSql;
42056
43454
  if (statements.length > 1) {
42057
- const plans = buildBatchExplainPlans(normalized.normalizedSql);
43455
+ const plans = await buildBatchExplainPlans(
43456
+ explainSourceSql,
43457
+ explainClient,
43458
+ void 0,
43459
+ explainCacheContext,
43460
+ runtime?.maxRecords
43461
+ );
42058
43462
  return {
42059
43463
  ok: true,
42060
43464
  batch: true,
@@ -42063,8 +43467,9 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42063
43467
  appBindings
42064
43468
  };
42065
43469
  }
42066
- const result = await executeSql(explainSql(normalized.normalizedSql), noOpClient(), {
42067
- cacheContext: normalized.cacheContext
43470
+ const result = await executeSql(explainSql(explainSourceSql), explainClient, {
43471
+ cacheContext: explainCacheContext,
43472
+ maxRecords: runtime?.maxRecords
42068
43473
  });
42069
43474
  if (result.type !== "SELECT") {
42070
43475
  throw new Error(`ArgumentError: EXPLAIN returned unexpected result type ${result.type}.`);
@@ -42089,7 +43494,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42089
43494
  profile: input.profile,
42090
43495
  maxRecords: input.maxRecords,
42091
43496
  fetchParallel: input.fetchParallel,
42092
- onLimit: validation.requiresCompleteInput ? "error" : input.onLimit,
43497
+ onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
42093
43498
  timeout: input.timeout,
42094
43499
  tempTableMaxRows: input.tempTableMaxRows
42095
43500
  });
@@ -42134,7 +43539,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42134
43539
  profile: input.profile,
42135
43540
  maxRecords: input.maxRecords,
42136
43541
  fetchParallel: input.fetchParallel,
42137
- onLimit: validation.requiresCompleteInput ? "error" : input.onLimit,
43542
+ onLimit: validation.containsValidationOnly ? "error" : input.onLimit,
42138
43543
  timeout: input.timeout
42139
43544
  });
42140
43545
  const result = await executeSql(runtime.sql, runtime.client, {
@@ -42430,7 +43835,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
42430
43835
  var profile = external_exports.string().min(1).describe("kintone connection profile name from ksql.config.json (default: the server's default profile).").optional();
42431
43836
  var maxRecords = external_exports.number().int().positive().describe("Maximum records fetched per SELECT (default 500).").optional();
42432
43837
  var fetchParallel = external_exports.number().int().min(1).max(10).describe("Number of parallel kintone record-fetch requests (1-10).").optional();
42433
- 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();
43838
+ 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();
42434
43839
  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();
42435
43840
  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();
42436
43841
  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).");
@@ -42554,7 +43959,7 @@ Options:
42554
43959
  -h, --help Show help
42555
43960
  `);
42556
43961
  }
42557
- var SERVER_VERSION = true ? "2.16.0" : "0.0.0-dev";
43962
+ var SERVER_VERSION = true ? "3.0.0" : "0.0.0-dev";
42558
43963
  function createServer(args) {
42559
43964
  const server = new McpServer({
42560
43965
  name: "ksql-mcp",
@@ -42571,12 +43976,12 @@ function createServer(args) {
42571
43976
  }, tools.validateTool);
42572
43977
  server.registerTool("ksql_explain", {
42573
43978
  title: "Explain kSQL",
42574
- description: "Return the kSQL execution plan without calling kintone APIs.",
43979
+ description: "Return the schema-aware kSQL execution plan. Reads form metadata and, when needed, process status metadata; never reads or writes records.",
42575
43980
  inputSchema: explainInputShape
42576
43981
  }, tools.explainTool);
42577
43982
  server.registerTool("ksql_query", {
42578
43983
  title: "Run read-only kSQL",
42579
- 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.",
43984
+ 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.",
42580
43985
  inputSchema: queryInputShape
42581
43986
  }, tools.queryTool);
42582
43987
  server.registerTool("ksql_mutate", {