@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.
package/dist-cli/ksql.js CHANGED
@@ -78,6 +78,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
78
78
  ["BY", "BY" /* BY */],
79
79
  ["HAVING", "HAVING" /* HAVING */],
80
80
  ["ORDER", "ORDER" /* ORDER */],
81
+ ["KORDER", "KORDER" /* KORDER */],
81
82
  ["ASC", "ASC" /* ASC */],
82
83
  ["DESC", "DESC" /* DESC */],
83
84
  ["LIMIT", "LIMIT" /* LIMIT */],
@@ -123,6 +124,11 @@ var KEYWORDS = /* @__PURE__ */ new Map([
123
124
  ["COALESCE", "COALESCE" /* COALESCE */],
124
125
  ["NULLIF", "NULLIF" /* NULLIF */],
125
126
  ["ISNULL", "ISNULL" /* ISNULL */],
127
+ ["INSTR", "INSTR" /* INSTR */],
128
+ ["GREATEST", "GREATEST" /* GREATEST */],
129
+ ["LEAST", "LEAST" /* LEAST */],
130
+ ["LPAD", "LPAD" /* LPAD */],
131
+ ["RPAD", "RPAD" /* RPAD */],
126
132
  ["CAST", "CAST" /* CAST */],
127
133
  ["CONVERT", "CONVERT" /* CONVERT */],
128
134
  ["FORMAT", "FORMAT" /* FORMAT */],
@@ -130,6 +136,8 @@ var KEYWORDS = /* @__PURE__ */ new Map([
130
136
  ["FLOOR", "FLOOR" /* FLOOR */],
131
137
  ["CEIL", "CEIL" /* CEIL */],
132
138
  ["CEILING", "CEILING" /* CEILING */],
139
+ ["TRUNCATE", "TRUNCATE" /* TRUNCATE */],
140
+ ["TRUNC", "TRUNC" /* TRUNC */],
133
141
  ["ABS", "ABS" /* ABS */],
134
142
  ["MOD", "MOD" /* MOD */],
135
143
  ["POWER", "POWER" /* POWER */],
@@ -141,6 +149,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
141
149
  ["DATE_FORMAT", "DATE_FORMAT" /* DATE_FORMAT */],
142
150
  ["DATEDIFF", "DATEDIFF" /* DATEDIFF */],
143
151
  ["DATE_ADD", "DATE_ADD" /* DATE_ADD */],
152
+ ["LAST_DAY", "LAST_DAY" /* LAST_DAY */],
144
153
  ["IF", "IF" /* IF */]
145
154
  ]);
146
155
 
@@ -486,6 +495,13 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
486
495
  "COALESCE" /* COALESCE */,
487
496
  "NULLIF" /* NULLIF */,
488
497
  "ISNULL" /* ISNULL */,
498
+ "LEFT" /* LEFT */,
499
+ "RIGHT" /* RIGHT */,
500
+ "INSTR" /* INSTR */,
501
+ "GREATEST" /* GREATEST */,
502
+ "LEAST" /* LEAST */,
503
+ "LPAD" /* LPAD */,
504
+ "RPAD" /* RPAD */,
489
505
  "CAST" /* CAST */,
490
506
  "CONVERT" /* CONVERT */,
491
507
  "FORMAT" /* FORMAT */,
@@ -493,6 +509,8 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
493
509
  "FLOOR" /* FLOOR */,
494
510
  "CEIL" /* CEIL */,
495
511
  "CEILING" /* CEILING */,
512
+ "TRUNCATE" /* TRUNCATE */,
513
+ "TRUNC" /* TRUNC */,
496
514
  "ABS" /* ABS */,
497
515
  "MOD" /* MOD */,
498
516
  "POWER" /* POWER */,
@@ -504,6 +522,7 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
504
522
  "DATE_FORMAT" /* DATE_FORMAT */,
505
523
  "DATEDIFF" /* DATEDIFF */,
506
524
  "DATE_ADD" /* DATE_ADD */,
525
+ "LAST_DAY" /* LAST_DAY */,
507
526
  "IF" /* IF */
508
527
  ]);
509
528
  function needsSpaceBetween(prev, cur) {
@@ -585,7 +604,7 @@ var Parser = class {
585
604
  case "WITH" /* WITH */:
586
605
  return this.parseWith();
587
606
  case "SELECT" /* SELECT */:
588
- return this.tryParseUnionChain(this.parseSelect());
607
+ return this.tryParseUnionChain(this.parseSelect(true));
589
608
  case "INSERT" /* INSERT */:
590
609
  return this.parseInsert();
591
610
  case "UPDATE" /* UPDATE */:
@@ -775,7 +794,7 @@ var Parser = class {
775
794
  }
776
795
  query = w;
777
796
  } else if (tok.kind === "SELECT" /* SELECT */) {
778
- const sel = this.parseSelect();
797
+ const sel = this.parseSelect(true);
779
798
  const chained = this.tryParseUnionChain(sel);
780
799
  query = chained;
781
800
  } else if (tok.kind === "INSERT" /* INSERT */) {
@@ -968,7 +987,7 @@ var Parser = class {
968
987
  // ----------------------------------------------------------
969
988
  // SELECT
970
989
  // ----------------------------------------------------------
971
- parseSelect() {
990
+ parseSelect(allowKorder = false) {
972
991
  this.expect("SELECT" /* SELECT */);
973
992
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
974
993
  const columns = this.parseSelectColumns();
@@ -985,7 +1004,19 @@ var Parser = class {
985
1004
  having = this.parseWhereExpr();
986
1005
  }
987
1006
  }
988
- const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
1007
+ let orderMode = "CANONICAL";
1008
+ let orderBy = [];
1009
+ if (this.consume("ORDER" /* ORDER */)) {
1010
+ this.expect("BY" /* BY */);
1011
+ orderBy = this.parseOrderBy();
1012
+ } else if (this.consume("KORDER" /* KORDER */)) {
1013
+ if (!allowKorder) {
1014
+ 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());
1015
+ }
1016
+ orderMode = "KINTONE_NATIVE";
1017
+ this.expect("BY" /* BY */);
1018
+ orderBy = this.parseOrderBy();
1019
+ }
989
1020
  const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
990
1021
  const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
991
1022
  const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
@@ -1002,6 +1033,7 @@ var Parser = class {
1002
1033
  where,
1003
1034
  groupBy,
1004
1035
  having,
1036
+ orderMode,
1005
1037
  orderBy,
1006
1038
  limit,
1007
1039
  offset
@@ -1039,6 +1071,9 @@ var Parser = class {
1039
1071
  // ----------------------------------------------------------
1040
1072
  tryParseUnionChain(left) {
1041
1073
  if (this.peek().kind !== "UNION" /* UNION */) return left;
1074
+ if (left.type === "SELECT" && left.orderMode === "KINTONE_NATIVE") {
1075
+ throw new ParseError("KORDER BY \u306F UNION \u5206\u5C90\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
1076
+ }
1042
1077
  this.advance();
1043
1078
  const all = this.consume("ALL" /* ALL */);
1044
1079
  const right = this.parseSelect();
@@ -1376,6 +1411,10 @@ var Parser = class {
1376
1411
  // arg: 文字列リテラル / 算術式(フィールド参照・数値含む)/ ネスト文字列関数
1377
1412
  // ──────────────────────────────────────────────────
1378
1413
  tryStringFuncName() {
1414
+ if (this.peekAt(1).kind === "(" /* LPAREN */) {
1415
+ if (this.peek().kind === "LEFT" /* LEFT */) return "LEFT";
1416
+ if (this.peek().kind === "RIGHT" /* RIGHT */) return "RIGHT";
1417
+ }
1379
1418
  const map = {
1380
1419
  ["UPPER" /* UPPER */]: "UPPER",
1381
1420
  ["LOWER" /* LOWER */]: "LOWER",
@@ -1390,6 +1429,11 @@ var Parser = class {
1390
1429
  ["COALESCE" /* COALESCE */]: "COALESCE",
1391
1430
  ["NULLIF" /* NULLIF */]: "NULLIF",
1392
1431
  ["ISNULL" /* ISNULL */]: "ISNULL",
1432
+ ["INSTR" /* INSTR */]: "INSTR",
1433
+ ["GREATEST" /* GREATEST */]: "GREATEST",
1434
+ ["LEAST" /* LEAST */]: "LEAST",
1435
+ ["LPAD" /* LPAD */]: "LPAD",
1436
+ ["RPAD" /* RPAD */]: "RPAD",
1393
1437
  ["CAST" /* CAST */]: "CAST",
1394
1438
  ["CONVERT" /* CONVERT */]: "CAST",
1395
1439
  // CONVERT → CAST に正規化
@@ -1399,12 +1443,16 @@ var Parser = class {
1399
1443
  ["CEIL" /* CEIL */]: "CEIL",
1400
1444
  ["CEILING" /* CEILING */]: "CEIL",
1401
1445
  // CEILING → CEIL に正規化
1446
+ ["TRUNCATE" /* TRUNCATE */]: "TRUNCATE",
1447
+ ["TRUNC" /* TRUNC */]: "TRUNCATE",
1448
+ // TRUNC → TRUNCATE に正規化
1402
1449
  ["YEAR" /* YEAR */]: "YEAR",
1403
1450
  ["MONTH" /* MONTH */]: "MONTH",
1404
1451
  ["DAY" /* DAY */]: "DAY",
1405
1452
  ["DATE_FORMAT" /* DATE_FORMAT */]: "DATE_FORMAT",
1406
1453
  ["DATEDIFF" /* DATEDIFF */]: "DATEDIFF",
1407
1454
  ["DATE_ADD" /* DATE_ADD */]: "DATE_ADD",
1455
+ ["LAST_DAY" /* LAST_DAY */]: "LAST_DAY",
1408
1456
  ["ABS" /* ABS */]: "ABS",
1409
1457
  ["MOD" /* MOD */]: "MOD",
1410
1458
  ["POWER" /* POWER */]: "POWER",
@@ -2679,7 +2727,50 @@ function isReadOnlyStatement(stmt) {
2679
2727
  return !writesKintone(stmt) && (isReadOnlyType(stmt.type) || isDmlType(stmt.type));
2680
2728
  }
2681
2729
  function requiresCompleteInput(stmt) {
2682
- return isDmlType(stmt.type);
2730
+ if (isDmlType(stmt.type)) return true;
2731
+ switch (stmt.type) {
2732
+ case "SELECT":
2733
+ return selectRequiresCompleteInput(stmt);
2734
+ case "UNION":
2735
+ return unionRequiresCompleteInput(stmt);
2736
+ case "WITH":
2737
+ return stmt.ctes.some(
2738
+ (cte) => cte.query.type === "SELECT" && selectRequiresCompleteInput(cte.query) || cte.query.type === "UNION" && unionRequiresCompleteInput(cte.query)
2739
+ ) || (stmt.query.type === "SELECT" ? selectRequiresCompleteInput(stmt.query) : unionRequiresCompleteInput(stmt.query));
2740
+ case "CREATE_TEMP_TABLE":
2741
+ return stmt.query.type === "SELECT" ? selectRequiresCompleteInput(stmt.query) : stmt.query.type === "UNION" ? unionRequiresCompleteInput(stmt.query) : requiresCompleteInput(stmt.query);
2742
+ default:
2743
+ return false;
2744
+ }
2745
+ }
2746
+ function unionRequiresCompleteInput(stmt) {
2747
+ const left = stmt.left.type === "SELECT" ? selectRequiresCompleteInput(stmt.left) : unionRequiresCompleteInput(stmt.left);
2748
+ return left || selectRequiresCompleteInput(stmt.right);
2749
+ }
2750
+ function selectRequiresCompleteInput(stmt) {
2751
+ if (stmt.orderBy.length > 0) return true;
2752
+ if (stmt.columns.some(
2753
+ (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(
2754
+ (branch) => whereRequiresCompleteInput(branch.condition)
2755
+ )
2756
+ )) return true;
2757
+ return whereRequiresCompleteInput(stmt.where) || whereRequiresCompleteInput(stmt.having);
2758
+ }
2759
+ function whereRequiresCompleteInput(where) {
2760
+ if (where === null) return false;
2761
+ switch (where.type) {
2762
+ case "BINARY":
2763
+ return (where.right.type === "SUBQUERY_IN_LIST" || where.right.type === "SCALAR_SUBQUERY") && selectRequiresCompleteInput(where.right.query);
2764
+ case "LOGICAL":
2765
+ return whereRequiresCompleteInput(where.left) || whereRequiresCompleteInput(where.right);
2766
+ case "NOT":
2767
+ case "GROUP":
2768
+ return whereRequiresCompleteInput(where.expr);
2769
+ case "EXISTS":
2770
+ return selectRequiresCompleteInput(where.query);
2771
+ case "NULL_CHECK":
2772
+ return false;
2773
+ }
2683
2774
  }
2684
2775
  function hasWhereClause(stmt) {
2685
2776
  if (!stmt || typeof stmt !== "object") return false;
@@ -3494,6 +3585,7 @@ function buildInlinedQuery(stmt) {
3494
3585
  where,
3495
3586
  groupBy: [],
3496
3587
  having: null,
3588
+ orderMode: "CANONICAL",
3497
3589
  orderBy: final.orderBy.length > 0 ? final.orderBy : cteBody.orderBy,
3498
3590
  limit: final.limit ?? cteBody.limit,
3499
3591
  offset: final.offset ?? cteBody.offset,
@@ -4088,6 +4180,72 @@ function analyzeBatch(statements) {
4088
4180
  };
4089
4181
  }
4090
4182
 
4183
+ // src/core/fieldSemantics.ts
4184
+ var STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
4185
+ "SINGLE_LINE_TEXT",
4186
+ "MULTI_LINE_TEXT",
4187
+ "RICH_TEXT",
4188
+ "LINK",
4189
+ "DATE",
4190
+ "TIME",
4191
+ "DATETIME",
4192
+ "CREATED_TIME",
4193
+ "UPDATED_TIME",
4194
+ "CREATOR",
4195
+ "MODIFIER"
4196
+ ]);
4197
+ var OPTION_FIELD_TYPES = /* @__PURE__ */ new Set([
4198
+ "DROP_DOWN",
4199
+ "RADIO_BUTTON",
4200
+ "CHECK_BOX",
4201
+ "MULTI_SELECT",
4202
+ "STATUS"
4203
+ ]);
4204
+ function resolveFieldSemantics(source) {
4205
+ let compareMode;
4206
+ if (source.fieldType === "RECORD_NUMBER" || source.fieldType === "__ID__") {
4207
+ compareMode = "recordNumber";
4208
+ } else if (source.fieldType === "NUMBER") {
4209
+ compareMode = "number";
4210
+ } else if (source.fieldType === "CALC") {
4211
+ compareMode = source.sortKind === "number" ? "number" : "string";
4212
+ } else if (OPTION_FIELD_TYPES.has(source.fieldType)) {
4213
+ compareMode = "option";
4214
+ } else if (STRING_FIELD_TYPES.has(source.fieldType)) {
4215
+ compareMode = "string";
4216
+ } else {
4217
+ compareMode = "unsupported";
4218
+ }
4219
+ const optionOrder = source.optionOrder ? new Map(Object.entries(source.optionOrder)) : void 0;
4220
+ return {
4221
+ fieldType: source.fieldType,
4222
+ compareMode,
4223
+ inSubtable: source.inSubtable === true,
4224
+ requiresCollectionOperators: source.inSubtable === true || source.requiresCollectionOperators === true,
4225
+ ...optionOrder && optionOrder.size > 0 ? { optionOrder } : {}
4226
+ };
4227
+ }
4228
+ function syntheticSemantics(compareMode, fieldType = compareMode === "number" ? "KSQL_NUMBER" : "KSQL_STRING") {
4229
+ return { fieldType, compareMode, inSubtable: false, requiresCollectionOperators: false };
4230
+ }
4231
+ function withFieldSemanticSource(semantics, appId, fieldCode) {
4232
+ return { ...semantics, source: { appId, fieldCode } };
4233
+ }
4234
+ function fieldSemanticsEqual(left, right) {
4235
+ if (left === right) return true;
4236
+ if (!left || !right) return false;
4237
+ if (left.fieldType !== right.fieldType || left.compareMode !== right.compareMode || left.inSubtable !== right.inSubtable || left.requiresCollectionOperators !== right.requiresCollectionOperators) return false;
4238
+ if (left.source?.appId !== right.source?.appId || left.source?.fieldCode !== right.source?.fieldCode) return false;
4239
+ const a = left.optionOrder;
4240
+ const b = right.optionOrder;
4241
+ if (a === b) return true;
4242
+ if (!a || !b || a.size !== b.size) return false;
4243
+ for (const [key, value] of a) {
4244
+ if (b.get(key) !== value) return false;
4245
+ }
4246
+ return true;
4247
+ }
4248
+
4091
4249
  // src/core/batchVariables.ts
4092
4250
  var VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
4093
4251
  function normalizeBatchVariableName(name) {
@@ -4123,26 +4281,197 @@ function validateDeclaredBatchVariables(statements, input) {
4123
4281
  }
4124
4282
 
4125
4283
  // src/core/scalarCompare.ts
4126
- function compareScalarValues(op, leftStr, rightStr) {
4127
- if (op === "=") return leftStr === rightStr;
4128
- if (op === "!=" || op === "<>") return leftStr !== rightStr;
4129
- const rightNum = Number(rightStr);
4130
- if (leftStr === "" && rightStr !== "" && Number.isFinite(rightNum)) {
4131
- return op === "<" || op === "<=";
4132
- }
4133
- const leftNum = Number(leftStr);
4134
- const numeric = !Number.isNaN(leftNum) && !Number.isNaN(rightNum);
4284
+ function compareCodePointStrings(left, right) {
4285
+ const a = left[Symbol.iterator]();
4286
+ const b = right[Symbol.iterator]();
4287
+ while (true) {
4288
+ const av = a.next();
4289
+ const bv = b.next();
4290
+ if (av.done || bv.done) {
4291
+ if (av.done && bv.done) return 0;
4292
+ return av.done ? -1 : 1;
4293
+ }
4294
+ const ac = av.value.codePointAt(0) ?? 0;
4295
+ const bc = bv.value.codePointAt(0) ?? 0;
4296
+ if (ac < bc) return -1;
4297
+ if (ac > bc) return 1;
4298
+ }
4299
+ }
4300
+ function triCompare(left, right) {
4301
+ return left < right ? -1 : left > right ? 1 : 0;
4302
+ }
4303
+ function numberKey(value) {
4304
+ if (value === "") return { band: 0 };
4305
+ const numeric = Number(value);
4306
+ if (numeric === Number.NEGATIVE_INFINITY) return { band: 1 };
4307
+ if (Number.isFinite(numeric)) return { band: 2, value: numeric };
4308
+ if (numeric === Number.POSITIVE_INFINITY) return { band: 3 };
4309
+ if (value === "NaN") return { band: 4 };
4310
+ return { band: 5, value };
4311
+ }
4312
+ function compareNumbers(left, right) {
4313
+ const a = numberKey(left);
4314
+ const b = numberKey(right);
4315
+ if (a.band !== b.band) return a.band < b.band ? -1 : 1;
4316
+ if (a.band === 2 && b.band === 2) return triCompare(a.value, b.value);
4317
+ if (a.band === 5 && b.band === 5) return compareCodePointStrings(a.value, b.value);
4318
+ return 0;
4319
+ }
4320
+ function recordNumberKey(value, allowPrefix) {
4321
+ if (value === "") return { empty: true, normalizedId: "", display: value };
4322
+ const match = /^\d+$/.test(value) ? value : allowPrefix ? /-(\d+)$/.exec(value)?.[1] : void 0;
4323
+ if (match === void 0) {
4324
+ throw new Error(`ArgumentError: invalid ${allowPrefix ? "RECORD_NUMBER" : "$id"} value: ${value}`);
4325
+ }
4326
+ return {
4327
+ empty: false,
4328
+ normalizedId: match.replace(/^0+(?=\d)/, ""),
4329
+ display: value
4330
+ };
4331
+ }
4332
+ function compareRecordNumbers(left, right, allowPrefix) {
4333
+ const a = recordNumberKey(left, allowPrefix);
4334
+ const b = recordNumberKey(right, allowPrefix);
4335
+ if (a.empty || b.empty) return a.empty === b.empty ? 0 : a.empty ? -1 : 1;
4336
+ if (a.normalizedId.length !== b.normalizedId.length) {
4337
+ return a.normalizedId.length < b.normalizedId.length ? -1 : 1;
4338
+ }
4339
+ const idCmp = compareCodePointStrings(a.normalizedId, b.normalizedId);
4340
+ return idCmp !== 0 ? idCmp : compareCodePointStrings(a.display, b.display);
4341
+ }
4342
+ function parseOptionValues(value, fieldType) {
4343
+ if (value === "") return [];
4344
+ if (fieldType !== "CHECK_BOX" && fieldType !== "MULTI_SELECT") return [value];
4345
+ try {
4346
+ const parsed = JSON.parse(value);
4347
+ return Array.isArray(parsed) ? parsed.map((item) => String(item ?? "")) : [value];
4348
+ } catch {
4349
+ return [value];
4350
+ }
4351
+ }
4352
+ function optionVector(value, semantics) {
4353
+ const order = semantics.optionOrder ?? /* @__PURE__ */ new Map();
4354
+ const unique = [...new Set(parseOptionValues(value, semantics.fieldType))];
4355
+ const vector = unique.map((label) => {
4356
+ const rank = order.get(label);
4357
+ return rank === void 0 ? { knownBand: 1, rank: 0, label } : { knownBand: 0, rank, label };
4358
+ });
4359
+ vector.sort(compareOptionElement);
4360
+ return vector;
4361
+ }
4362
+ function compareOptionElement(left, right) {
4363
+ if (left.knownBand !== right.knownBand) return left.knownBand < right.knownBand ? -1 : 1;
4364
+ if (left.rank !== right.rank) return left.rank < right.rank ? -1 : 1;
4365
+ return compareCodePointStrings(left.label, right.label);
4366
+ }
4367
+ function compareOptions(left, right, semantics) {
4368
+ const a = optionVector(left, semantics);
4369
+ const b = optionVector(right, semantics);
4370
+ const length = Math.min(a.length, b.length);
4371
+ for (let index = 0; index < length; index++) {
4372
+ const cmp = compareOptionElement(a[index], b[index]);
4373
+ if (cmp !== 0) return cmp;
4374
+ }
4375
+ return a.length < b.length ? -1 : a.length > b.length ? 1 : 0;
4376
+ }
4377
+ function compareCanonicalValues(left, right, semantics) {
4378
+ switch (semantics.compareMode) {
4379
+ case "string":
4380
+ return compareCodePointStrings(left, right);
4381
+ case "number":
4382
+ return compareNumbers(left, right);
4383
+ case "recordNumber":
4384
+ return compareRecordNumbers(left, right, semantics.fieldType === "RECORD_NUMBER");
4385
+ case "option":
4386
+ return compareOptions(left, right, semantics);
4387
+ case "unsupported":
4388
+ throw new Error(`ArgumentError: values of type ${semantics.fieldType} cannot be compared.`);
4389
+ }
4390
+ }
4391
+ function compareScalarValues(op, left, right, semantics = syntheticSemantics("string")) {
4392
+ const cmp = compareCanonicalValues(left, right, semantics);
4135
4393
  switch (op) {
4394
+ case "=":
4395
+ return cmp === 0;
4396
+ case "!=":
4397
+ case "<>":
4398
+ return cmp !== 0;
4136
4399
  case ">":
4137
- return numeric ? leftNum > rightNum : leftStr > rightStr;
4400
+ return cmp > 0;
4138
4401
  case "<":
4139
- return numeric ? leftNum < rightNum : leftStr < rightStr;
4402
+ return cmp < 0;
4140
4403
  case ">=":
4141
- return numeric ? leftNum >= rightNum : leftStr >= rightStr;
4404
+ return cmp >= 0;
4142
4405
  case "<=":
4143
- return numeric ? leftNum <= rightNum : leftStr <= rightStr;
4406
+ return cmp <= 0;
4407
+ }
4408
+ }
4409
+ function selectScalarExtreme(values, extreme) {
4410
+ if (extreme === "least" && values.includes("")) return "";
4411
+ const candidates = extreme === "greatest" ? values.filter((value) => value !== "") : [...values];
4412
+ if (candidates.length === 0) return "";
4413
+ const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
4414
+ const compare = (left, right) => {
4415
+ if (numeric) {
4416
+ const numericCmp = triCompare(Number(left), Number(right));
4417
+ if (numericCmp !== 0) return numericCmp;
4418
+ }
4419
+ return compareCodePointStrings(left, right);
4420
+ };
4421
+ return candidates.reduce((best, candidate) => {
4422
+ const cmp = compare(candidate, best);
4423
+ return extreme === "greatest" ? cmp > 0 ? candidate : best : cmp < 0 ? candidate : best;
4424
+ });
4425
+ }
4426
+
4427
+ // src/core/explainMetadata.ts
4428
+ function whereNeedsFieldMetadata(where) {
4429
+ if (where === null) return false;
4430
+ switch (where.type) {
4431
+ case "BINARY":
4432
+ return valueNeedsFieldMetadata(where.left);
4433
+ case "NULL_CHECK":
4434
+ return valueNeedsFieldMetadata(where.field);
4435
+ case "LOGICAL":
4436
+ return whereNeedsFieldMetadata(where.left) || whereNeedsFieldMetadata(where.right);
4437
+ case "NOT":
4438
+ case "GROUP":
4439
+ return whereNeedsFieldMetadata(where.expr);
4440
+ case "EXISTS":
4441
+ return false;
4144
4442
  }
4145
4443
  }
4444
+ function valueNeedsFieldMetadata(value) {
4445
+ if (Array.isArray(value)) return value.some(valueNeedsFieldMetadata);
4446
+ if (value === null || typeof value !== "object") return false;
4447
+ const item = value;
4448
+ if (item["type"] === "FIELD") return item["field"] !== "$id";
4449
+ if (item["type"] === "SELECT") return false;
4450
+ return Object.values(item).some(valueNeedsFieldMetadata);
4451
+ }
4452
+ function selectNeedsOwnMetadata(statement) {
4453
+ return whereNeedsFieldMetadata(statement.where) || statement.orderBy.length > 0 || statement.columns.some(
4454
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
4455
+ );
4456
+ }
4457
+ function explainNeedsAppMetadata(statement) {
4458
+ const seen = /* @__PURE__ */ new Set();
4459
+ const visit = (node) => {
4460
+ if (node === null || typeof node !== "object") return false;
4461
+ if (seen.has(node)) return false;
4462
+ seen.add(node);
4463
+ if (Array.isArray(node)) return node.some(visit);
4464
+ const item = node;
4465
+ if (item["type"] === "SELECT" && selectNeedsOwnMetadata(node)) {
4466
+ return true;
4467
+ }
4468
+ if ((item["type"] === "UPDATE" || item["type"] === "DELETE") && whereNeedsFieldMetadata(node.where)) {
4469
+ return true;
4470
+ }
4471
+ return Object.values(item).some(visit);
4472
+ };
4473
+ return visit(statement);
4474
+ }
4146
4475
 
4147
4476
  // src/engine/evalFunc.ts
4148
4477
  function evalArithExpr(expr, row) {
@@ -4191,6 +4520,36 @@ function evalStringFunc(expr, row) {
4191
4520
  const len = args[2] !== void 0 ? Number(args[2]) : void 0;
4192
4521
  return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
4193
4522
  }
4523
+ case "LEFT": {
4524
+ assertArity("LEFT", args, 2, 2);
4525
+ const str = args[0];
4526
+ const n = Math.trunc(Number(args[1]));
4527
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
4528
+ }
4529
+ case "RIGHT": {
4530
+ assertArity("RIGHT", args, 2, 2);
4531
+ const str = args[0];
4532
+ const n = Math.trunc(Number(args[1]));
4533
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
4534
+ }
4535
+ case "INSTR":
4536
+ assertArity("INSTR", args, 2, 2);
4537
+ return String(args[0].indexOf(args[1]) + 1);
4538
+ case "LPAD":
4539
+ case "RPAD": {
4540
+ assertArity(expr.func, args, 2, 3);
4541
+ const str = args[0];
4542
+ const n = Math.trunc(Number(args[1]));
4543
+ if (Number.isNaN(n) || n <= 0) return "";
4544
+ if (str.length >= n) return str.slice(0, n);
4545
+ const pad = args[2] ?? " ";
4546
+ if (pad === "") return str;
4547
+ return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
4548
+ }
4549
+ case "GREATEST":
4550
+ case "LEAST":
4551
+ assertArity(expr.func, args, 2);
4552
+ return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
4194
4553
  case "CONCAT":
4195
4554
  return args.join("");
4196
4555
  case "REPLACE": {
@@ -4211,6 +4570,9 @@ function evalStringFunc(expr, row) {
4211
4570
  return applyRoundOp("floor", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
4212
4571
  case "CEIL":
4213
4572
  return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
4573
+ case "TRUNCATE":
4574
+ assertArity("TRUNCATE", args, 1, 2);
4575
+ return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
4214
4576
  case "CAST": {
4215
4577
  const val = args[0] ?? "";
4216
4578
  const castType = args[1] ?? "TEXT";
@@ -4243,6 +4605,9 @@ function evalStringFunc(expr, row) {
4243
4605
  return applyDateDiff(args[0] ?? "", args[1] ?? "");
4244
4606
  case "DATE_ADD":
4245
4607
  return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
4608
+ case "LAST_DAY":
4609
+ assertArity("LAST_DAY", args, 1, 1);
4610
+ return applyLastDay(args[0]);
4246
4611
  case "ABS":
4247
4612
  return String(Math.abs(Number(args[0] ?? "0")));
4248
4613
  case "MOD": {
@@ -4265,6 +4630,11 @@ function evalStringFunc(expr, row) {
4265
4630
  return (/* @__PURE__ */ new Date()).toISOString();
4266
4631
  }
4267
4632
  }
4633
+ function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
4634
+ if (args.length >= min && args.length <= max) return;
4635
+ const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
4636
+ throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
4637
+ }
4268
4638
  function parseDateParts(s) {
4269
4639
  return {
4270
4640
  y: s.slice(0, 4) || "0000",
@@ -4289,6 +4659,9 @@ function applyDateDiff(date1, date2) {
4289
4659
  return String(Math.round((d1 - d2) / 864e5));
4290
4660
  }
4291
4661
  function applyDateAdd(dateStr, n, unit) {
4662
+ if (unit !== "YEAR" && unit !== "MONTH" && unit !== "DAY") {
4663
+ throw new Error("ArgumentError: DATE_ADD unit must be YEAR, MONTH, or DAY.");
4664
+ }
4292
4665
  if (!dateStr || dateStr.length < 10) return dateStr;
4293
4666
  const { y, mo, d } = parseDateParts(dateStr);
4294
4667
  const dt = new Date(Date.UTC(+y, +mo - 1, +d));
@@ -4299,7 +4672,7 @@ function applyDateAdd(dateStr, n, unit) {
4299
4672
  case "MONTH":
4300
4673
  dt.setUTCMonth(dt.getUTCMonth() + n);
4301
4674
  break;
4302
- default:
4675
+ case "DAY":
4303
4676
  dt.setUTCDate(dt.getUTCDate() + n);
4304
4677
  break;
4305
4678
  }
@@ -4308,6 +4681,15 @@ function applyDateAdd(dateStr, n, unit) {
4308
4681
  const rd = String(dt.getUTCDate()).padStart(2, "0");
4309
4682
  return `${ry}-${rmo}-${rd}`;
4310
4683
  }
4684
+ function applyLastDay(dateStr) {
4685
+ if (!dateStr || dateStr.length < 10) return dateStr;
4686
+ const { y, mo } = parseDateParts(dateStr);
4687
+ const dt = new Date(Date.UTC(+y, +mo, 0));
4688
+ const ry = String(dt.getUTCFullYear()).padStart(4, "0");
4689
+ const rmo = String(dt.getUTCMonth() + 1).padStart(2, "0");
4690
+ const rd = String(dt.getUTCDate()).padStart(2, "0");
4691
+ return `${ry}-${rmo}-${rd}`;
4692
+ }
4311
4693
  function applyFormat(num, pattern) {
4312
4694
  if (/^-?\d+$/.test(pattern.trim())) {
4313
4695
  return formatWithComma(num, Math.max(0, Number(pattern)));
@@ -4355,34 +4737,35 @@ function resolveFieldRef(row, field) {
4355
4737
  }
4356
4738
 
4357
4739
  // src/engine/evalWhere.ts
4358
- function evalWhere(expr, row, resolveFieldType, appliedKlikes) {
4740
+ function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
4359
4741
  switch (expr.type) {
4360
4742
  case "BINARY":
4361
- return evalBinary(expr, row, resolveFieldType, appliedKlikes);
4743
+ return evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
4362
4744
  case "NULL_CHECK":
4363
4745
  return evalNullCheck(expr, row);
4364
4746
  case "LOGICAL":
4365
- return evalLogical(expr, row, resolveFieldType, appliedKlikes);
4747
+ return evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
4366
4748
  case "NOT":
4367
- return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
4749
+ return !evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
4368
4750
  case "GROUP":
4369
- return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes);
4751
+ return evalWhere(expr.expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
4370
4752
  case "EXISTS": {
4371
4753
  const exists = expr.resolved;
4372
4754
  return expr.not ? !exists : exists;
4373
4755
  }
4374
4756
  }
4375
4757
  }
4376
- function evalBinary(expr, row, resolveFieldType, appliedKlikes) {
4758
+ function evalBinary(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
4377
4759
  if (expr.op === "KLIKE" || expr.op === "NOT_KLIKE") {
4378
4760
  if (appliedKlikes?.has(expr)) return true;
4379
4761
  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");
4380
4762
  }
4381
- const left = resolveField(expr.left, row, resolveFieldType);
4763
+ const left = resolveField(expr.left, row, resolveFieldType, resolveFieldSemantics2);
4382
4764
  const fieldType = expr.left.type === "FIELD" ? resolveFieldType?.(expr.left) : void 0;
4383
- return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType);
4765
+ const semantics = semanticsForLeft(expr.left, fieldType, resolveFieldSemantics2);
4766
+ return evalOp(expr.op, left, expr.right, row, fieldType, resolveFieldType, semantics, resolveFieldSemantics2);
4384
4767
  }
4385
- function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
4768
+ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType, semantics = syntheticSemantics("string"), resolveFieldSemantics2) {
4386
4769
  if (op === "IN" || op === "NOT_IN") {
4387
4770
  let values = null;
4388
4771
  if (right.type === "IN_LIST") {
@@ -4407,8 +4790,53 @@ function evalOp(op, leftStr, right, row, fieldType, resolveFieldType) {
4407
4790
  if (op === "KLIKE" || op === "NOT_KLIKE") {
4408
4791
  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");
4409
4792
  }
4410
- const rightStr = resolveValue(right, row, resolveFieldType);
4411
- return compareScalarValues(op, leftStr, rightStr);
4793
+ const rightStr = resolveValue(right, row, resolveFieldType, resolveFieldSemantics2);
4794
+ return compareScalarValues(op, leftStr, rightStr, semantics);
4795
+ }
4796
+ var NUMERIC_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
4797
+ "LENGTH",
4798
+ "INSTR",
4799
+ "ROUND",
4800
+ "FLOOR",
4801
+ "CEIL",
4802
+ "TRUNCATE",
4803
+ "YEAR",
4804
+ "MONTH",
4805
+ "DAY",
4806
+ "DATEDIFF",
4807
+ "ABS",
4808
+ "MOD",
4809
+ "POWER",
4810
+ "SQRT"
4811
+ ]);
4812
+ function semanticsForLeft(left, fieldType, resolveSemantics) {
4813
+ if (left.type === "FIELD") {
4814
+ return resolveSemantics?.(left) ?? (fieldType ? resolveFieldSemantics({ fieldType }) : syntheticSemantics("string"));
4815
+ }
4816
+ if (left.type === "ARITH_FIELD") return syntheticSemantics("number");
4817
+ if (left.type === "FUNC_FIELD") {
4818
+ return syntheticSemantics(NUMERIC_STRING_FUNCTIONS.has(left.expr.func) ? "number" : "string");
4819
+ }
4820
+ if (left.type === "CASE_FIELD") {
4821
+ const results = [
4822
+ ...left.expr.branches.map((branch) => branch.result),
4823
+ ...left.expr.elseResult ? [left.expr.elseResult] : []
4824
+ ];
4825
+ const modes = results.map((result) => {
4826
+ if (result.type === "NUMBER" || result.type === "ARITH") return syntheticSemantics("number");
4827
+ if (result.type === "STRING_FUNC") {
4828
+ return syntheticSemantics(NUMERIC_STRING_FUNCTIONS.has(result.func) ? "number" : "string");
4829
+ }
4830
+ if (result.type === "FIELD_REF") {
4831
+ const dot = result.field.indexOf(".");
4832
+ 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 };
4833
+ return resolveSemantics?.(ref) ?? syntheticSemantics("string");
4834
+ }
4835
+ return syntheticSemantics("string");
4836
+ });
4837
+ if (modes.length > 0 && modes.every((mode) => mode.compareMode === modes[0].compareMode)) return modes[0];
4838
+ }
4839
+ return syntheticSemantics("string");
4412
4840
  }
4413
4841
  var STRING_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set(["CHECK_BOX", "MULTI_SELECT"]);
4414
4842
  var OBJECT_ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([
@@ -4459,20 +4887,20 @@ function evalNullCheck(expr, row) {
4459
4887
  const val = resolveField(expr.field, row);
4460
4888
  return expr.not ? val !== "" : val === "";
4461
4889
  }
4462
- function evalLogical(expr, row, resolveFieldType, appliedKlikes) {
4890
+ function evalLogical(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
4463
4891
  if (expr.op === "AND") {
4464
- return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
4892
+ return evalWhere(expr.left, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) && evalWhere(expr.right, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
4465
4893
  }
4466
- return evalWhere(expr.left, row, resolveFieldType, appliedKlikes) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes);
4894
+ return evalWhere(expr.left, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) || evalWhere(expr.right, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2);
4467
4895
  }
4468
- function resolveField(field, row, resolveFieldType) {
4896
+ function resolveField(field, row, resolveFieldType, resolveFieldSemantics2) {
4469
4897
  if (field.type === "FUNC_FIELD") return evalStringFunc(field.expr, row);
4470
4898
  if (field.type === "ARITH_FIELD") return String(evalArithExpr(field.expr, row));
4471
- if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType);
4899
+ if (field.type === "CASE_FIELD") return evalCaseWhen(field.expr, row, resolveFieldType, resolveFieldSemantics2);
4472
4900
  const key = field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
4473
4901
  return resolveFieldRef(row, key);
4474
4902
  }
4475
- function resolveValue(value, row, resolveFieldType) {
4903
+ function resolveValue(value, row, resolveFieldType, resolveFieldSemantics2) {
4476
4904
  switch (value.type) {
4477
4905
  case "VARIABLE":
4478
4906
  throw new Error(`ParseError: unresolved batch variable @${value.name}.`);
@@ -4495,14 +4923,14 @@ function resolveValue(value, row, resolveFieldType) {
4495
4923
  if (value.expr.type === "STRING_FUNC") return evalStringFunc(value.expr, row);
4496
4924
  return String(evalArithExpr(value.expr, row));
4497
4925
  case "CASE_VALUE":
4498
- return evalCaseWhen(value.expr, row, resolveFieldType);
4926
+ return evalCaseWhen(value.expr, row, resolveFieldType, resolveFieldSemantics2);
4499
4927
  case "ARRAY":
4500
4928
  return value.elements.map((e) => e.value).join(",");
4501
4929
  }
4502
4930
  }
4503
- function evalCaseWhen(expr, row, resolveFieldType) {
4931
+ function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
4504
4932
  for (const branch of expr.branches) {
4505
- if (evalWhere(branch.condition, row, resolveFieldType)) {
4933
+ if (evalWhere(branch.condition, row, resolveFieldType, void 0, resolveFieldSemantics2)) {
4506
4934
  return evalCaseResult(branch.result, row);
4507
4935
  }
4508
4936
  }
@@ -5133,6 +5561,141 @@ async function resolveDmlTargetIds(getRecords, app, query, options) {
5133
5561
  };
5134
5562
  }
5135
5563
 
5564
+ // src/core/optimization/canonicalOrderPlanner.ts
5565
+ var REST_OFFSET_MAX = 1e4;
5566
+ var REST_LIMIT_MAX = 500;
5567
+ function fieldSemantics(item, semantics) {
5568
+ return item.key.type === "FIELD_NAME" ? semantics.get(item.key.name) : void 0;
5569
+ }
5570
+ function planCanonicalOrder(input) {
5571
+ const { stmt } = input;
5572
+ const reasons = [];
5573
+ const windowOrderBy = stmt.columns.flatMap(
5574
+ (column) => column.type === "WINDOW_COL" ? column.orderBy : []
5575
+ );
5576
+ const allOrderBy = [...stmt.orderBy, ...windowOrderBy];
5577
+ for (const item of allOrderBy) {
5578
+ if (item.key.type !== "FIELD_NAME") continue;
5579
+ const semantics = fieldSemantics(item, input.orderSemantics);
5580
+ if (!semantics) {
5581
+ reasons.push("ORDER_KEY_UNRESOLVED");
5582
+ continue;
5583
+ }
5584
+ if (semantics.fieldType === "KSQL_AMBIGUOUS") reasons.push("ORDER_KEY_AMBIGUOUS");
5585
+ else if (semantics.compareMode === "unsupported") reasons.push("ORDER_KEY_UNSUPPORTED");
5586
+ }
5587
+ if (reasons.includes("ORDER_KEY_AMBIGUOUS")) {
5588
+ throw new Error(
5589
+ "ArgumentError: ORDER BY key is an ambiguous column reference (reason=ORDER_KEY_AMBIGUOUS). Qualify the key with its table alias."
5590
+ );
5591
+ }
5592
+ if (reasons.includes("ORDER_KEY_UNSUPPORTED") || reasons.includes("ORDER_KEY_UNRESOLVED")) {
5593
+ const reason = reasons.includes("ORDER_KEY_UNSUPPORTED") ? "ORDER_KEY_UNSUPPORTED" : "ORDER_KEY_UNRESOLVED";
5594
+ throw new Error(`ArgumentError: ORDER BY key has no canonical comparison contract (reason=${reason}).`);
5595
+ }
5596
+ const allRestEquivalent = stmt.orderBy.length > 0 && windowOrderBy.length === 0 && stmt.orderBy.every(
5597
+ (item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
5598
+ );
5599
+ if (!allRestEquivalent) reasons.push("ORDER_KEY_NOT_REST_EQUIVALENT");
5600
+ if (input.whereCapability !== "EXACT_PUSHDOWN") reasons.push("WHERE_NOT_EXACT");
5601
+ if (input.staticMode !== "SIMPLE") reasons.push("QUERY_SHAPE_LOCAL");
5602
+ if (stmt.limit === null || stmt.limit < 0 || stmt.limit > REST_LIMIT_MAX) {
5603
+ reasons.push("LIMIT_NOT_REST_WINDOW");
5604
+ }
5605
+ if ((stmt.offset ?? 0) < 0 || (stmt.offset ?? 0) > REST_OFFSET_MAX) {
5606
+ reasons.push("OFFSET_NOT_REST_WINDOW");
5607
+ }
5608
+ if (stmt.limit !== null && stmt.limit > input.maxRecords) reasons.push("MAX_RECORDS_WINDOW");
5609
+ if (input.hasKlike) reasons.push("KLIKE_NOT_REST_WINDOW");
5610
+ if (reasons.length === 0) {
5611
+ return {
5612
+ kind: "CANONICAL_REST_TOP_N",
5613
+ requiresCompleteInput: false,
5614
+ localOrderBy: false,
5615
+ applyLocalOffsetLimit: false,
5616
+ reasonCodes: []
5617
+ };
5618
+ }
5619
+ return {
5620
+ kind: "CANONICAL_LOCAL",
5621
+ requiresCompleteInput: allOrderBy.length > 0,
5622
+ localOrderBy: stmt.orderBy.length > 0,
5623
+ applyLocalOffsetLimit: stmt.orderBy.length > 0,
5624
+ reasonCodes: [...new Set(reasons)]
5625
+ };
5626
+ }
5627
+
5628
+ // src/core/optimization/korderPlanner.ts
5629
+ var KORDER_NATIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
5630
+ "RECORD_NUMBER",
5631
+ "SINGLE_LINE_TEXT",
5632
+ "NUMBER",
5633
+ "CALC",
5634
+ "DATE",
5635
+ "DATETIME",
5636
+ "TIME",
5637
+ "CREATED_TIME",
5638
+ "UPDATED_TIME",
5639
+ "DROP_DOWN",
5640
+ "RADIO_BUTTON",
5641
+ "STATUS",
5642
+ "LINK",
5643
+ "CREATOR",
5644
+ "MODIFIER"
5645
+ ]);
5646
+ function planKorderNative(input) {
5647
+ const { stmt } = input;
5648
+ const reasons = [];
5649
+ if (stmt.orderMode !== "KINTONE_NATIVE") reasons.push("KORDER_MODE_REQUIRED");
5650
+ if (stmt.from.cteName !== null || stmt.from.subtableCode || input.staticMode !== "SIMPLE") {
5651
+ reasons.push("KORDER_QUERY_SHAPE_UNSUPPORTED");
5652
+ }
5653
+ if (input.whereCapability !== "EXACT_PUSHDOWN") reasons.push("KORDER_WHERE_NOT_EXACT");
5654
+ if (input.hasKlike) reasons.push("KORDER_KLIKE_UNSUPPORTED");
5655
+ if (stmt.orderBy.length === 0) reasons.push("KORDER_KEY_REQUIRED");
5656
+ for (const item of stmt.orderBy) {
5657
+ if (item.key.type !== "FIELD_NAME") {
5658
+ reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(key=${item.key.type})`);
5659
+ continue;
5660
+ }
5661
+ const name = item.key.name;
5662
+ const semantics = input.orderSemantics.get(name);
5663
+ if (!semantics) {
5664
+ reasons.push(`KORDER_KEY_UNRESOLVED(field=${name})`);
5665
+ continue;
5666
+ }
5667
+ if (name === "$id") continue;
5668
+ if (!semantics.source || semantics.source.fieldCode !== name) {
5669
+ reasons.push(`KORDER_KEY_NOT_DIRECT_FIELD(field=${name})`);
5670
+ continue;
5671
+ }
5672
+ if (!KORDER_NATIVE_FIELD_TYPES.has(semantics.fieldType)) {
5673
+ reasons.push(`KORDER_TYPE_UNSUPPORTED(field=${name}, type=${semantics.fieldType})`);
5674
+ }
5675
+ }
5676
+ if (stmt.limit === null || stmt.limit < 0 || stmt.limit > 500) {
5677
+ reasons.push(`KORDER_LIMIT_INVALID(limit=${String(stmt.limit)})`);
5678
+ }
5679
+ if (stmt.limit !== null && stmt.limit > input.maxRecords) {
5680
+ reasons.push(`KORDER_LIMIT_EXCEEDS_MAX_RECORDS(limit=${stmt.limit}, maxRecords=${input.maxRecords})`);
5681
+ }
5682
+ const offset = stmt.offset ?? 0;
5683
+ if (offset < 0 || offset > 1e4) reasons.push(`KORDER_OFFSET_INVALID(offset=${offset})`);
5684
+ const unique = [...new Set(reasons)];
5685
+ if (unique.length > 0) {
5686
+ throw new Error(
5687
+ `ArgumentError: KORDER BY cannot be executed (mode=KINTONE_NATIVE; ${unique.join(", ")}). Use ORDER BY for canonical local ordering or simplify the query.`
5688
+ );
5689
+ }
5690
+ return {
5691
+ kind: "KORDER_NATIVE",
5692
+ requiresCompleteInput: false,
5693
+ localOrderBy: false,
5694
+ applyLocalOffsetLimit: false,
5695
+ reasonCodes: []
5696
+ };
5697
+ }
5698
+
5136
5699
  // src/engine/process.ts
5137
5700
  function flatten(record, alias) {
5138
5701
  const row = {};
@@ -5197,9 +5760,9 @@ function applyJoin(leftRows, rightRows, join2) {
5197
5760
  }
5198
5761
  return result;
5199
5762
  }
5200
- function applyFilter(rows, where, resolveFieldType, appliedKlikes) {
5763
+ function applyFilter(rows, where, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
5201
5764
  if (where === null) return rows;
5202
- return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes));
5765
+ return rows.filter((row) => evalWhere(where, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2));
5203
5766
  }
5204
5767
  function hasAggregateColumns(columns) {
5205
5768
  return columns.some(
@@ -5260,7 +5823,7 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
5260
5823
  let strVal;
5261
5824
  if (arg.type === "FIELD_REF") {
5262
5825
  const raw = row[arg.field];
5263
- if (raw === void 0 || raw === "") continue;
5826
+ if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
5264
5827
  strVal = raw;
5265
5828
  } else {
5266
5829
  const n = evalArithExpr(arg, row);
@@ -5272,10 +5835,16 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
5272
5835
  const eff = distinct ? [...new Set(strValues)] : strValues;
5273
5836
  if (func === "COUNT") return eff.length;
5274
5837
  if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
5275
- const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
5276
- if (sortKind === "string") {
5277
- if (eff.length === 0) return "";
5278
- return func === "MAX" ? maxStringOf(eff) : minStringOf(eff);
5838
+ const comparison = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
5839
+ if (func === "MIN" || func === "MAX") {
5840
+ if (eff.length === 0) return 0;
5841
+ const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" ? syntheticSemantics("string") : syntheticSemantics("number"));
5842
+ let result = eff[0];
5843
+ for (const candidate of eff.slice(1)) {
5844
+ const cmp = compareCanonicalValues(candidate, result, semantics);
5845
+ if (func === "MAX" && cmp > 0 || func === "MIN" && cmp < 0) result = candidate;
5846
+ }
5847
+ return result;
5279
5848
  }
5280
5849
  const nums = eff.map(Number);
5281
5850
  switch (func) {
@@ -5283,37 +5852,12 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
5283
5852
  return nums.reduce((a, b) => a + b, 0);
5284
5853
  case "AVG":
5285
5854
  return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length;
5286
- // Math.max(...nums) は要素数が多いと RangeError になるためループで求める
5287
- case "MAX":
5288
- return nums.length === 0 ? 0 : maxOf(nums);
5289
- case "MIN":
5290
- return nums.length === 0 ? 0 : minOf(nums);
5291
5855
  }
5292
5856
  }
5293
5857
  function toAggregateFieldRef(field) {
5294
5858
  const dot = field.indexOf(".");
5295
5859
  return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
5296
5860
  }
5297
- function maxStringOf(values) {
5298
- let value = values[0];
5299
- for (const candidate of values) if (candidate > value) value = candidate;
5300
- return value;
5301
- }
5302
- function minStringOf(values) {
5303
- let value = values[0];
5304
- for (const candidate of values) if (candidate < value) value = candidate;
5305
- return value;
5306
- }
5307
- function maxOf(nums) {
5308
- let m = nums[0];
5309
- for (const n of nums) if (n > m) m = n;
5310
- return m;
5311
- }
5312
- function minOf(nums) {
5313
- let m = nums[0];
5314
- for (const n of nums) if (n < m) m = n;
5315
- return m;
5316
- }
5317
5861
  function evalAggArithExpr(node, rows, resolveAggSortKind) {
5318
5862
  if (node.type === "NUMBER") return node.value;
5319
5863
  if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
@@ -5345,9 +5889,9 @@ function aggregateSyntheticName2(func, distinct, arg) {
5345
5889
  const argStr = aggregateArgLabel(arg);
5346
5890
  return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
5347
5891
  }
5348
- function applyHaving(rows, having, resolveFieldType) {
5892
+ function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
5349
5893
  if (having === null) return rows;
5350
- return rows.filter((row) => evalWhere(having, row, resolveFieldType));
5894
+ return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
5351
5895
  }
5352
5896
  function applyDistinct(rows, columns) {
5353
5897
  if (rows.length === 0) return rows;
@@ -5399,27 +5943,37 @@ function buildDistinctKeyBuilder(rows, columns) {
5399
5943
  return JSON.stringify(values);
5400
5944
  };
5401
5945
  }
5402
- function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
5946
+ function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
5403
5947
  if (orderBy.length === 0) return rows;
5404
- return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
5405
- }
5406
- function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
5407
- const keyMeta = orderBy.map(({ key }) => ({
5408
- orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
5409
- sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
5410
- }));
5948
+ return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2).rows.map((item) => item.row);
5949
+ }
5950
+ function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds, fieldSemantics2) {
5951
+ const keyMeta = orderBy.map(({ key }) => {
5952
+ if (key.type === "ARITH_KEY") return { semantics: syntheticSemantics("number") };
5953
+ if (key.type === "FUNC_KEY") {
5954
+ return { semantics: syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(key.expr.func) ? "number" : "string") };
5955
+ }
5956
+ const semantics = fieldSemantics2?.get(key.name);
5957
+ if (semantics) return { semantics };
5958
+ const orderMap = optionOrders?.get(key.name);
5959
+ if (orderMap) {
5960
+ return {
5961
+ semantics: {
5962
+ fieldType: "MULTI_SELECT",
5963
+ compareMode: "option",
5964
+ inSubtable: false,
5965
+ requiresCollectionOperators: false,
5966
+ optionOrder: orderMap
5967
+ }
5968
+ };
5969
+ }
5970
+ return { semantics: syntheticSemantics(sortKinds?.get(key.name) ?? "string") };
5971
+ });
5411
5972
  const decorated = rows.map((row) => ({
5412
5973
  row,
5413
5974
  keys: orderBy.map(({ key }, i) => {
5414
5975
  const s = evalOrderKey(key, row);
5415
- const n = Number(s);
5416
- const orderMap = keyMeta[i].orderMap;
5417
- return {
5418
- s,
5419
- n,
5420
- isNum: !Number.isNaN(n),
5421
- rank: orderMap ? minChoiceIndex(parseChoiceValues(s), orderMap) : 0
5422
- };
5976
+ return { s };
5423
5977
  })
5424
5978
  }));
5425
5979
  const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
@@ -5434,15 +5988,24 @@ function compareDecoratedRows(a, b, orderBy, keyMeta) {
5434
5988
  return 0;
5435
5989
  }
5436
5990
  function compareSortKeys(a, b, meta) {
5437
- if (meta.orderMap) {
5438
- if (a.rank !== b.rank) return a.rank - b.rank;
5439
- return a.s.localeCompare(b.s, "ja");
5440
- }
5441
- if (meta.sortKind === "string") {
5442
- return a.s.localeCompare(b.s, "ja");
5443
- }
5444
- return a.isNum && b.isNum ? a.n - b.n : a.s.localeCompare(b.s, "ja");
5445
- }
5991
+ return compareCanonicalValues(a.s, b.s, meta.semantics);
5992
+ }
5993
+ var NUMERIC_ORDER_FUNCTIONS = /* @__PURE__ */ new Set([
5994
+ "LENGTH",
5995
+ "INSTR",
5996
+ "ROUND",
5997
+ "FLOOR",
5998
+ "CEIL",
5999
+ "TRUNCATE",
6000
+ "YEAR",
6001
+ "MONTH",
6002
+ "DAY",
6003
+ "DATEDIFF",
6004
+ "ABS",
6005
+ "MOD",
6006
+ "POWER",
6007
+ "SQRT"
6008
+ ]);
5446
6009
  function evalOrderKey(key, row) {
5447
6010
  switch (key.type) {
5448
6011
  case "FIELD_NAME":
@@ -5453,30 +6016,7 @@ function evalOrderKey(key, row) {
5453
6016
  return evalStringFunc(key.expr, row);
5454
6017
  }
5455
6018
  }
5456
- function parseChoiceValues(raw) {
5457
- const trimmed = raw.trim();
5458
- if (trimmed === "") return [""];
5459
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
5460
- try {
5461
- const arr = JSON.parse(trimmed);
5462
- if (Array.isArray(arr)) {
5463
- return arr.map((v) => String(v ?? ""));
5464
- }
5465
- } catch {
5466
- }
5467
- }
5468
- return [trimmed];
5469
- }
5470
- function minChoiceIndex(values, orderMap) {
5471
- let min = Number.MAX_SAFE_INTEGER;
5472
- for (const value of values) {
5473
- const idx = orderMap.get(value);
5474
- const rank = idx ?? Number.MAX_SAFE_INTEGER;
5475
- if (rank < min) min = rank;
5476
- }
5477
- return min;
5478
- }
5479
- function applyWindow(rows, columns, optionOrders, sortKinds) {
6019
+ function applyWindow(rows, columns, optionOrders, sortKinds, fieldSemantics2) {
5480
6020
  const windows = columns.filter((column) => column.type === "WINDOW_COL");
5481
6021
  if (rows.length === 0 || windows.length === 0) return rows;
5482
6022
  for (const window of windows) {
@@ -5488,7 +6028,7 @@ function applyWindow(rows, columns, optionOrders, sortKinds) {
5488
6028
  else partitions.set(key, [row]);
5489
6029
  }
5490
6030
  for (const partition of partitions.values()) {
5491
- const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
6031
+ const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds, fieldSemantics2);
5492
6032
  const sorted = sortedResult.rows;
5493
6033
  let rank = 1;
5494
6034
  let denseRank = 1;
@@ -5513,7 +6053,7 @@ function applyLimit(rows, limit, offset) {
5513
6053
  if (limit === null) return rows.slice(start);
5514
6054
  return rows.slice(start, start + limit);
5515
6055
  }
5516
- function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
6056
+ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2) {
5517
6057
  if (columns.length === 1 && columns[0].type === "WILDCARD") {
5518
6058
  const projected2 = rows.map((row) => stripParentShortcutColumns(row));
5519
6059
  const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
@@ -5577,7 +6117,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
5577
6117
  }
5578
6118
  case "CASE_COL": {
5579
6119
  const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
5580
- out[key] = evalCaseWhen(col.expr, row, resolveFieldType);
6120
+ out[key] = evalCaseWhen(col.expr, row, resolveFieldType, resolveFieldSemantics2);
5581
6121
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
5582
6122
  break;
5583
6123
  }
@@ -5732,6 +6272,26 @@ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
5732
6272
  args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
5733
6273
  };
5734
6274
  }
6275
+ function deriveOutputOrderSemantics(columns) {
6276
+ const result = /* @__PURE__ */ new Map();
6277
+ for (const column of columns) {
6278
+ if (!("alias" in column) || !column.alias) continue;
6279
+ if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
6280
+ result.set(column.alias, syntheticSemantics("number"));
6281
+ } else if (column.type === "AGGREGATE") {
6282
+ if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
6283
+ result.set(column.alias, syntheticSemantics("number"));
6284
+ } else if (column.func === "GROUP_CONCAT") {
6285
+ result.set(column.alias, syntheticSemantics("string"));
6286
+ }
6287
+ } else if (column.type === "LITERAL_COL" || column.type === "CASE_COL" || column.type === "SCALAR_SUBQUERY_COL") {
6288
+ result.set(column.alias, syntheticSemantics("string"));
6289
+ } else if (column.type === "STRFUNC_COL") {
6290
+ result.set(column.alias, syntheticSemantics(NUMERIC_ORDER_FUNCTIONS.has(column.expr.func) ? "number" : "string"));
6291
+ }
6292
+ }
6293
+ return result;
6294
+ }
5735
6295
  function runFullScan(input) {
5736
6296
  const {
5737
6297
  stmt,
@@ -5739,12 +6299,17 @@ function runFullScan(input) {
5739
6299
  scalarCache,
5740
6300
  optionOrders,
5741
6301
  sortKinds,
6302
+ orderSemantics,
5742
6303
  fieldTypeResolver,
6304
+ fieldSemanticsResolver,
5743
6305
  havingFieldTypeResolver,
6306
+ havingFieldSemanticsResolver,
5744
6307
  aggregateSortKindResolver,
5745
6308
  appliedKlikes,
5746
6309
  sourceColumns
5747
6310
  } = input;
6311
+ const effectiveOrderSemantics = deriveOutputOrderSemantics(stmt.columns);
6312
+ for (const [key, value] of orderSemantics ?? []) effectiveOrderSemantics.set(key, value);
5748
6313
  let rows = [];
5749
6314
  const mainAlias = stmt.from.alias;
5750
6315
  const mainRecords = tables.get(mainAlias) ?? tables.get(null) ?? [];
@@ -5755,18 +6320,18 @@ function runFullScan(input) {
5755
6320
  const rightRows = rightRecords.map((r) => flatten(r, rightAlias));
5756
6321
  rows = applyJoin(rows, rightRows, join2);
5757
6322
  }
5758
- rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
6323
+ rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes, fieldSemanticsResolver);
5759
6324
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
5760
6325
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
5761
6326
  }
5762
- rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
5763
- rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
6327
+ rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
6328
+ rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
5764
6329
  if (stmt.distinct) {
5765
6330
  rows = applyDistinct(rows, stmt.columns);
5766
6331
  }
5767
- rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
6332
+ rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds, effectiveOrderSemantics);
5768
6333
  rows = applyLimit(rows, stmt.limit, stmt.offset);
5769
- return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns);
6334
+ return project(rows, stmt.columns, scalarCache, fieldTypeResolver, sourceColumns, fieldSemanticsResolver);
5770
6335
  }
5771
6336
 
5772
6337
  // src/converter/subtableAdapter.ts
@@ -6030,8 +6595,194 @@ function renderValidationValue(value) {
6030
6595
  if (sql.type === "STRING" || sql.type === "NUMBER") return String(sql.value ?? "");
6031
6596
  if (sql.type === "ARRAY") return JSON.stringify(sql.elements?.map((e) => e.value) ?? []);
6032
6597
  }
6033
- if (Array.isArray(value)) return JSON.stringify(value);
6034
- return String(value);
6598
+ if (Array.isArray(value)) return JSON.stringify(value);
6599
+ return String(value);
6600
+ }
6601
+
6602
+ // src/core/optimization/whereCapability.ts
6603
+ var RANGE_AND_EQUALITY = ["=", "!=", ">", "<", ">=", "<="];
6604
+ var EQUALITY_IN = ["=", "!=", "in", "not in"];
6605
+ var NATIVE_OPERATORS = /* @__PURE__ */ new Map([
6606
+ ["RECORD_NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
6607
+ ["__ID__", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
6608
+ ["CREATOR", /* @__PURE__ */ new Set(["in", "not in"])],
6609
+ ["MODIFIER", /* @__PURE__ */ new Set(["in", "not in"])],
6610
+ ["CREATED_TIME", new Set(RANGE_AND_EQUALITY)],
6611
+ ["UPDATED_TIME", new Set(RANGE_AND_EQUALITY)],
6612
+ ["DATE", new Set(RANGE_AND_EQUALITY)],
6613
+ ["TIME", new Set(RANGE_AND_EQUALITY)],
6614
+ ["DATETIME", new Set(RANGE_AND_EQUALITY)],
6615
+ ["SINGLE_LINE_TEXT", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
6616
+ ["LINK", /* @__PURE__ */ new Set(["=", "!=", "in", "not in", "like", "not like"])],
6617
+ ["NUMBER", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
6618
+ ["CALC", /* @__PURE__ */ new Set([...RANGE_AND_EQUALITY, "in", "not in"])],
6619
+ ["MULTI_LINE_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
6620
+ ["RICH_TEXT", /* @__PURE__ */ new Set(["like", "not like"])],
6621
+ ["CHECK_BOX", /* @__PURE__ */ new Set(["in", "not in"])],
6622
+ ["RADIO_BUTTON", /* @__PURE__ */ new Set(["in", "not in"])],
6623
+ ["DROP_DOWN", /* @__PURE__ */ new Set(["in", "not in"])],
6624
+ ["MULTI_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
6625
+ ["FILE", /* @__PURE__ */ new Set(["like", "not like"])],
6626
+ ["USER_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
6627
+ ["ORGANIZATION_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
6628
+ ["GROUP_SELECT", /* @__PURE__ */ new Set(["in", "not in"])],
6629
+ ["STATUS", new Set(EQUALITY_IN)]
6630
+ ]);
6631
+ var LOCAL_SCALAR_TYPES = /* @__PURE__ */ new Set([
6632
+ "RECORD_NUMBER",
6633
+ "__ID__",
6634
+ "CREATOR",
6635
+ "MODIFIER",
6636
+ "CREATED_TIME",
6637
+ "UPDATED_TIME",
6638
+ "DATE",
6639
+ "TIME",
6640
+ "DATETIME",
6641
+ "SINGLE_LINE_TEXT",
6642
+ "LINK",
6643
+ "NUMBER",
6644
+ "CALC",
6645
+ "MULTI_LINE_TEXT",
6646
+ "RICH_TEXT",
6647
+ "RADIO_BUTTON",
6648
+ "DROP_DOWN",
6649
+ "STATUS",
6650
+ // 一時表・CTE・式列は kintone REST へは送らず、共有ローカル評価器で扱う。
6651
+ "KSQL_STRING",
6652
+ "KSQL_NUMBER",
6653
+ "KSQL_BOOLEAN"
6654
+ ]);
6655
+ var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
6656
+ "CHECK_BOX",
6657
+ "MULTI_SELECT",
6658
+ "FILE",
6659
+ "USER_SELECT",
6660
+ "ORGANIZATION_SELECT",
6661
+ "GROUP_SELECT",
6662
+ "STATUS_ASSIGNEE",
6663
+ "CATEGORY"
6664
+ ]);
6665
+ function nativeWhereOperatorsForType(fieldType) {
6666
+ return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
6667
+ }
6668
+ function classifyWhereCapability(where, resolveField2) {
6669
+ if (where === null) {
6670
+ return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
6671
+ }
6672
+ return classifyNode(where, resolveField2);
6673
+ }
6674
+ function classifyNode(where, resolveField2) {
6675
+ switch (where.type) {
6676
+ case "BINARY":
6677
+ return classifyBinary(where.op, where.left, where.right.type, resolveField2);
6678
+ case "NULL_CHECK":
6679
+ if (where.field.type !== "FIELD") return localExpression();
6680
+ return classifyLocalOnlyField(where.field, where.not ? "IS NOT NULL" : "IS NULL", resolveField2);
6681
+ case "EXISTS":
6682
+ return localExpression();
6683
+ case "GROUP":
6684
+ return classifyNode(where.expr, resolveField2);
6685
+ case "NOT": {
6686
+ const inner = classifyNode(where.expr, resolveField2);
6687
+ return inner.capability === "SUPERSET_PREFILTER" ? { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] } : inner;
6688
+ }
6689
+ case "LOGICAL": {
6690
+ const left = classifyNode(where.left, resolveField2);
6691
+ const right = classifyNode(where.right, resolveField2);
6692
+ return combineLogical(where.op, left, right);
6693
+ }
6694
+ }
6695
+ }
6696
+ function classifyBinary(op, left, rightType, resolveField2) {
6697
+ if (left.type !== "FIELD") return localExpression();
6698
+ const semantics = resolveField2(left);
6699
+ if (!semantics) {
6700
+ return unsupported("WHERE_FIELD_UNRESOLVED", left.field, void 0, normalizeOperator(op));
6701
+ }
6702
+ if (!hasLocalContract(semantics.fieldType, op)) {
6703
+ return unsupported("WHERE_OPERATOR_UNSUPPORTED", left.field, semantics.fieldType, normalizeOperator(op));
6704
+ }
6705
+ const nativeOp = normalizeOperator(op);
6706
+ const native = nativeWhereOperatorsForType(semantics.fieldType);
6707
+ const rightCanPush = rightType === "STRING" || rightType === "NUMBER" || rightType === "IN_LIST" || rightType === "KINTONE_FUNC";
6708
+ const structureAllows = !semantics.requiresCollectionOperators || nativeOp !== "=" && nativeOp !== "!=";
6709
+ const sqlLikeIsResidual = op === "LIKE" || op === "NOT_LIKE";
6710
+ if (rightCanPush && structureAllows && native.has(nativeOp) && !sqlLikeIsResidual) {
6711
+ return {
6712
+ capability: "EXACT_PUSHDOWN",
6713
+ reasons: [{
6714
+ code: "WHERE_EXACT",
6715
+ field: left.field,
6716
+ fieldType: semantics.fieldType,
6717
+ operator: nativeOp
6718
+ }]
6719
+ };
6720
+ }
6721
+ return {
6722
+ capability: "LOCAL_ONLY",
6723
+ reasons: [{
6724
+ code: "WHERE_RESIDUAL",
6725
+ field: left.field,
6726
+ fieldType: semantics.fieldType,
6727
+ operator: nativeOp
6728
+ }]
6729
+ };
6730
+ }
6731
+ function classifyLocalOnlyField(field, operator, resolveField2) {
6732
+ const semantics = resolveField2(field);
6733
+ if (!semantics) return unsupported("WHERE_FIELD_UNRESOLVED", field.field, void 0, operator);
6734
+ if (!LOCAL_SCALAR_TYPES.has(semantics.fieldType) && !LOCAL_COLLECTION_TYPES.has(semantics.fieldType)) {
6735
+ return unsupported("WHERE_OPERATOR_UNSUPPORTED", field.field, semantics.fieldType, operator);
6736
+ }
6737
+ return {
6738
+ capability: "LOCAL_ONLY",
6739
+ reasons: [{ code: "WHERE_RESIDUAL", field: field.field, fieldType: semantics.fieldType, operator }]
6740
+ };
6741
+ }
6742
+ function hasLocalContract(fieldType, op) {
6743
+ if (LOCAL_SCALAR_TYPES.has(fieldType)) return true;
6744
+ if (!LOCAL_COLLECTION_TYPES.has(fieldType)) return false;
6745
+ return op === "=" || op === "!=" || op === "<>" || op === "IN" || op === "NOT_IN" || op === "LIKE" || op === "NOT_LIKE" || op === "KLIKE" || op === "NOT_KLIKE";
6746
+ }
6747
+ function normalizeOperator(op) {
6748
+ switch (op) {
6749
+ case "<>":
6750
+ return "!=";
6751
+ case "IN":
6752
+ return "in";
6753
+ case "NOT_IN":
6754
+ return "not in";
6755
+ case "LIKE":
6756
+ case "KLIKE":
6757
+ return "like";
6758
+ case "NOT_LIKE":
6759
+ case "NOT_KLIKE":
6760
+ return "not like";
6761
+ default:
6762
+ return op;
6763
+ }
6764
+ }
6765
+ function combineLogical(op, left, right) {
6766
+ const reasons = [...left.reasons, ...right.reasons];
6767
+ if (left.capability === "UNSUPPORTED" || right.capability === "UNSUPPORTED") {
6768
+ return { capability: "UNSUPPORTED", reasons };
6769
+ }
6770
+ if (left.capability === "EXACT_PUSHDOWN" && right.capability === "EXACT_PUSHDOWN") {
6771
+ return { capability: "EXACT_PUSHDOWN", reasons };
6772
+ }
6773
+ if (op === "AND" && (left.capability === "EXACT_PUSHDOWN" || right.capability === "EXACT_PUSHDOWN" || left.capability === "SUPERSET_PREFILTER" || right.capability === "SUPERSET_PREFILTER")) {
6774
+ return {
6775
+ capability: "SUPERSET_PREFILTER",
6776
+ reasons: [{ code: "WHERE_SUPERSET_PREFILTER" }, ...reasons]
6777
+ };
6778
+ }
6779
+ return { capability: "LOCAL_ONLY", reasons };
6780
+ }
6781
+ function localExpression() {
6782
+ return { capability: "LOCAL_ONLY", reasons: [{ code: "WHERE_EXPRESSION_LOCAL_ONLY" }] };
6783
+ }
6784
+ function unsupported(code, field, fieldType, operator) {
6785
+ return { capability: "UNSUPPORTED", reasons: [{ code, field, fieldType, operator }] };
6035
6786
  }
6036
6787
 
6037
6788
  // src/execute.ts
@@ -6044,8 +6795,20 @@ var SearchAbortedError = class extends Error {
6044
6795
  };
6045
6796
  var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
6046
6797
  var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
6798
+ var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
6799
+ var nextDefaultCacheContextId = 1;
6800
+ function resolveCacheContext(client, explicit) {
6801
+ if (explicit) return explicit;
6802
+ let context = defaultCacheContextByClient.get(client);
6803
+ if (!context) {
6804
+ context = `client:${nextDefaultCacheContextId++}`;
6805
+ defaultCacheContextByClient.set(client, context);
6806
+ }
6807
+ return context;
6808
+ }
6047
6809
  async function execute(sql, client, options = {}) {
6048
6810
  const startedAt = Date.now();
6811
+ const cacheContext = resolveCacheContext(client, options.cacheContext);
6049
6812
  const stmt = parseSql(sql);
6050
6813
  const metrics = createEmptyMetrics();
6051
6814
  const countedClient = wrapClientWithMetrics(client, metrics);
@@ -6059,7 +6822,7 @@ async function execute(sql, client, options = {}) {
6059
6822
  stmt,
6060
6823
  guardedClient,
6061
6824
  options,
6062
- options.cacheContext ?? "default"
6825
+ cacheContext
6063
6826
  );
6064
6827
  metrics.elapsedMs = Date.now() - startedAt;
6065
6828
  return { ...attachSearchAbortWarning(result, collector), metrics };
@@ -6174,7 +6937,7 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
6174
6937
  case "DESCRIBE":
6175
6938
  return executeDescribe(stmt, client, cacheContext);
6176
6939
  case "EXPLAIN":
6177
- return executeExplain(stmt);
6940
+ return executeExplain(stmt, client, cacheContext, options.maxRecords ?? 1e4);
6178
6941
  // 一時テーブルはバッチスコープのため単文実行では拒否する(executeBatch を使う)
6179
6942
  case "CREATE_TEMP_TABLE":
6180
6943
  throw new Error("ArgumentError: CREATE TEMP TABLE requires a batch (temp tables are batch-scoped).");
@@ -6205,7 +6968,7 @@ function materializedColumnMetaEqual(left, right) {
6205
6968
  if (!left || !right || left.size !== right.size) return false;
6206
6969
  for (const [column, meta] of left) {
6207
6970
  const candidate = right.get(column);
6208
- if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType) return false;
6971
+ if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType || !fieldSemanticsEqual(candidate.semantics, meta.semantics)) return false;
6209
6972
  }
6210
6973
  return true;
6211
6974
  }
@@ -6236,7 +6999,7 @@ async function executeBatch(sql, client, options = {}) {
6236
6999
  const countedClient = wrapClientWithMetrics(client, metrics);
6237
7000
  const startedAt = Date.now();
6238
7001
  const deadline = options.timeoutMs != null ? startedAt + options.timeoutMs : null;
6239
- const cacheContext = options.cacheContext ?? "default";
7002
+ const cacheContext = resolveCacheContext(client, options.cacheContext);
6240
7003
  const tempTables = /* @__PURE__ */ new Map();
6241
7004
  const variables = /* @__PURE__ */ new Map();
6242
7005
  const results = [];
@@ -6330,7 +7093,10 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
6330
7093
  cacheContext,
6331
7094
  tempTables
6332
7095
  );
6333
- variables.set(stmt.name, { type: "string", value });
7096
+ const first = resolvedStmt2.expr.query.columns[0];
7097
+ 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");
7098
+ const numberValue = numeric ? Number(value) : Number.NaN;
7099
+ variables.set(stmt.name, numeric && Number.isFinite(numberValue) ? { type: "number", value: numberValue } : { type: "string", value });
6334
7100
  } catch (e) {
6335
7101
  if (e instanceof ScalarSubqueryError) {
6336
7102
  throw new Error(`ArgumentError: ${e.message}`);
@@ -6561,13 +7327,14 @@ var ScalarSubqueryError = class extends Error {
6561
7327
  };
6562
7328
  async function executeAssert(stmt, client, options, cacheContext, tempTables) {
6563
7329
  const left = await evalAssertOperand(stmt.left, client, options, cacheContext, tempTables);
7330
+ const semantics = stmt.left.type === "NUMBER" || stmt.left.type === "ARITH" ? syntheticSemantics("number") : syntheticSemantics("string");
6564
7331
  if (stmt.op === "BETWEEN") {
6565
7332
  if (stmt.low === null || stmt.high === null) {
6566
7333
  throw new Error("ArgumentError: malformed ASSERT statement.");
6567
7334
  }
6568
7335
  const low = await evalAssertOperand(stmt.low, client, options, cacheContext, tempTables);
6569
7336
  const high = await evalAssertOperand(stmt.high, client, options, cacheContext, tempTables);
6570
- if (!compareScalarValues(">=", left, low) || !compareScalarValues("<=", left, high)) {
7337
+ if (!compareScalarValues(">=", left, low, semantics) || !compareScalarValues("<=", left, high, semantics)) {
6571
7338
  throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
6572
7339
  }
6573
7340
  return { type: "ASSERT", condition: stmt.text };
@@ -6576,7 +7343,7 @@ async function executeAssert(stmt, client, options, cacheContext, tempTables) {
6576
7343
  throw new Error("ArgumentError: malformed ASSERT statement.");
6577
7344
  }
6578
7345
  const right = await evalAssertOperand(stmt.right, client, options, cacheContext, tempTables);
6579
- if (!compareScalarValues(stmt.op, left, right)) {
7346
+ if (!compareScalarValues(stmt.op, left, right, semantics)) {
6580
7347
  throw new AssertError(`assertion failed: ${stmt.text} (actual: ${left}).`);
6581
7348
  }
6582
7349
  return { type: "ASSERT", condition: stmt.text };
@@ -6649,6 +7416,149 @@ function evalAssertArith(node) {
6649
7416
  }
6650
7417
  throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
6651
7418
  }
7419
+ async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables, forcePhysicalMetadata = false) {
7420
+ const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
7421
+ const physicalAppIds = forcePhysicalMetadata || whereNeedsFieldMetadata(stmt.where) ? [...new Set(tables.filter((table) => table.cteName === null).map((table) => table.appId))] : [];
7422
+ const infosByApp = new Map(
7423
+ await Promise.all(physicalAppIds.map(async (appId) => {
7424
+ const infos = await getFieldsCached(appId, client, cacheContext);
7425
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
7426
+ }))
7427
+ );
7428
+ const orderedFields = /* @__PURE__ */ new Set();
7429
+ const collectOrderedFields = (node) => {
7430
+ if (Array.isArray(node)) {
7431
+ node.forEach(collectOrderedFields);
7432
+ return;
7433
+ }
7434
+ if (node === null || typeof node !== "object") return;
7435
+ const value = node;
7436
+ if (value["type"] === "SELECT") return;
7437
+ if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
7438
+ const left = value["left"];
7439
+ if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
7440
+ orderedFields.add(left["field"]);
7441
+ }
7442
+ }
7443
+ Object.values(value).forEach(collectOrderedFields);
7444
+ };
7445
+ collectOrderedFields(stmt.where);
7446
+ collectOrderedFields(stmt.having);
7447
+ for (const column of stmt.columns) {
7448
+ if (column.type === "CASE_COL") collectOrderedFields(column.expr);
7449
+ }
7450
+ const statusOrdersByApp = /* @__PURE__ */ new Map();
7451
+ await Promise.all([...infosByApp].map(async ([appId, infos]) => {
7452
+ const needsStatus = [...orderedFields].some((field) => infos.get(field)?.fieldType === "STATUS");
7453
+ if (!needsStatus) return;
7454
+ const order = await loadProcessStatusOrder(appId, client, cacheContext);
7455
+ if (order) statusOrdersByApp.set(appId, order);
7456
+ }));
7457
+ const fromPhysical = (table, field) => {
7458
+ if (field === "$id") return withFieldSemanticSource(
7459
+ resolveFieldSemantics({ fieldType: "__ID__" }),
7460
+ table.appId,
7461
+ "$id"
7462
+ );
7463
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, field));
7464
+ if (!info) return void 0;
7465
+ const base = info.semantics ?? resolveFieldSemantics(info);
7466
+ const semantics = info.fieldType === "STATUS" && statusOrdersByApp.has(table.appId) ? { ...base, optionOrder: statusOrdersByApp.get(table.appId) } : base;
7467
+ return withFieldSemanticSource(
7468
+ semantics,
7469
+ table.appId,
7470
+ info.code
7471
+ );
7472
+ };
7473
+ return (field) => {
7474
+ if (field.tableAlias !== null) {
7475
+ if (field.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
7476
+ return fromPhysical(stmt.from, field.field);
7477
+ }
7478
+ const table = tables.find((candidate) => candidate.alias === field.tableAlias);
7479
+ if (!table) return void 0;
7480
+ if (table.cteName !== null) {
7481
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
7482
+ }
7483
+ return fromPhysical(table, field.field);
7484
+ }
7485
+ if (stmt.joins.length === 0) {
7486
+ if (stmt.from.cteName !== null) {
7487
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(field.field)?.semantics ?? syntheticSemantics("string");
7488
+ }
7489
+ return fromPhysical(stmt.from, field.field);
7490
+ }
7491
+ const matches = tables.flatMap((table) => {
7492
+ const semantics = table.cteName !== null ? materializedTables?.get(table.cteName)?.columnMeta?.get(field.field)?.semantics : fromPhysical(table, field.field);
7493
+ return semantics ? [semantics] : [];
7494
+ });
7495
+ if (matches.length === 1) return matches[0];
7496
+ return matches.length > 1 ? syntheticSemantics("string") : void 0;
7497
+ };
7498
+ }
7499
+ function selectCaseConditionsNeedFieldMetadata(stmt) {
7500
+ return stmt.columns.some((column) => column.type === "CASE_COL" && column.expr.branches.some((branch) => whereNeedsFieldMetadata(branch.condition)));
7501
+ }
7502
+ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
7503
+ const aliases = /* @__PURE__ */ new Map();
7504
+ for (const column of stmt.columns) {
7505
+ if (!("alias" in column) || !column.alias) continue;
7506
+ let semantics;
7507
+ if (column.type === "FIELD") semantics = rowResolver(aggregateFieldRef(column.field));
7508
+ else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
7509
+ semantics = syntheticSemantics("number");
7510
+ } else if (column.type === "AGGREGATE") {
7511
+ if (column.func === "MIN" || column.func === "MAX") {
7512
+ semantics = column.arg.type === "FIELD_REF" ? rowResolver(aggregateFieldRef(column.arg.field)) : syntheticSemantics("number");
7513
+ } else {
7514
+ semantics = column.func === "GROUP_CONCAT" ? syntheticSemantics("string") : syntheticSemantics("number");
7515
+ }
7516
+ } else if (column.type === "STRFUNC_COL") {
7517
+ semantics = stringFunctionColumnMeta(column.expr).semantics;
7518
+ } else if (column.type === "LITERAL_COL" || column.type === "SCALAR_SUBQUERY_COL" || column.type === "CASE_COL") {
7519
+ semantics = syntheticSemantics("string");
7520
+ }
7521
+ if (semantics) aliases.set(column.alias, semantics);
7522
+ }
7523
+ return (field) => field.tableAlias === null && aliases.has(field.field) ? aliases.get(field.field) : rowResolver(field);
7524
+ }
7525
+ async function resolveSelectWhereCapability(stmt, client, cacheContext, materializedTables) {
7526
+ if (stmt.where === null) return classifyWhereCapability(null, () => void 0);
7527
+ const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables);
7528
+ return classifyWhereCapability(stmt.where, resolver);
7529
+ }
7530
+ function formatWhereCapabilityFailure(result) {
7531
+ const reason = result.reasons.find(
7532
+ (candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED"
7533
+ ) ?? result.reasons[0];
7534
+ const details = [
7535
+ reason?.field ? `field=${reason.field}` : null,
7536
+ reason?.fieldType ? `type=${reason.fieldType}` : null,
7537
+ reason?.operator ? `operator=${reason.operator}` : null,
7538
+ reason?.code ? `reason=${reason.code}` : null
7539
+ ].filter((value) => value !== null).join(", ");
7540
+ return details || "reason=WHERE_UNSUPPORTED";
7541
+ }
7542
+ function hasCanonicalOrder(stmt) {
7543
+ return stmt.orderBy.length > 0 || stmt.columns.some(
7544
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
7545
+ );
7546
+ }
7547
+ async function assertDmlWhereCapability(stmt, client, cacheContext) {
7548
+ if (stmt.subtableCode || stmt.type === "UPDATE" && stmt.from != null) return;
7549
+ const fields = whereNeedsFieldMetadata(stmt.where) ? await getFieldsCached(stmt.appId, client, cacheContext) : [];
7550
+ const byCode = new Map(fields.map((field) => [field.code, field]));
7551
+ const result = classifyWhereCapability(stmt.where, (field) => {
7552
+ if (field.field === "$id") return resolveFieldSemantics({ fieldType: "__ID__" });
7553
+ const info = byCode.get(field.field);
7554
+ return info?.semantics ?? (info ? resolveFieldSemantics(info) : void 0);
7555
+ });
7556
+ if (result.capability !== "EXACT_PUSHDOWN") {
7557
+ throw new DmlConvertError(
7558
+ `WHERE predicate cannot be represented by kintone REST (${formatWhereCapabilityFailure(result)})`
7559
+ );
7560
+ }
7561
+ }
6652
7562
  async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
6653
7563
  let result;
6654
7564
  if (isNoFromSelect(stmt)) {
@@ -6659,12 +7569,51 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
6659
7569
  return result;
6660
7570
  }
6661
7571
  await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
6662
- const mode = resolveSelectMode(stmt);
6663
- await validateSelectFieldCodes(stmt, mode, client, cacheContext);
6664
- if (mode === "SIMPLE") {
6665
- result = await executeSimpleSelect(stmt, client, options, cacheContext);
6666
- } else {
6667
- result = await executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
7572
+ const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
7573
+ if (whereCapability.capability === "UNSUPPORTED") {
7574
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
7575
+ }
7576
+ const staticMode = resolveSelectMode(stmt);
7577
+ const mode = whereCapability.capability === "EXACT_PUSHDOWN" ? staticMode : "FULL_SCAN";
7578
+ const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
7579
+ const orderPlan = hasCanonicalOrder(stmt) ? (stmt.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
7580
+ stmt,
7581
+ staticMode: mode,
7582
+ whereCapability: whereCapability.capability,
7583
+ orderSemantics: orderMeta.semantics,
7584
+ maxRecords: options.maxRecords ?? 1e4,
7585
+ hasKlike: whereHasKlike(stmt.where)
7586
+ }) : null;
7587
+ await validateSelectFieldCodes(
7588
+ stmt,
7589
+ orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : mode,
7590
+ client,
7591
+ cacheContext
7592
+ );
7593
+ const completeInputRequired = orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" ? requiresCompleteInput({ ...stmt, orderBy: [] }) : requiresCompleteInput(stmt);
7594
+ const truncateWasDisabled = completeInputRequired && options.onLimitReached === "truncate";
7595
+ const effectiveOptions = truncateWasDisabled ? { ...options, onLimitReached: "error" } : options;
7596
+ try {
7597
+ if (mode === "SIMPLE") {
7598
+ result = await executeSimpleSelect(stmt, client, effectiveOptions, cacheContext, orderPlan, orderMeta);
7599
+ } else {
7600
+ result = await executeFullScanSelect(
7601
+ stmt,
7602
+ client,
7603
+ effectiveOptions,
7604
+ cacheContext,
7605
+ cteCache,
7606
+ whereCapability.capability === "EXACT_PUSHDOWN",
7607
+ orderMeta
7608
+ );
7609
+ }
7610
+ } catch (error) {
7611
+ if (completeInputRequired && error instanceof FetchAllLimitError) {
7612
+ throw new FetchAllLimitError(
7613
+ "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" : "") + error.message
7614
+ );
7615
+ }
7616
+ throw error;
6668
7617
  }
6669
7618
  if (captureColumnMeta) {
6670
7619
  materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
@@ -6725,19 +7674,30 @@ function executeNoFromSelect(stmt) {
6725
7674
  const rows = applyLimit(projected, stmt.limit, stmt.offset);
6726
7675
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
6727
7676
  }
6728
- async function executeSimpleSelect(stmt, client, options, cacheContext) {
6729
- const params = selectToKintoneParams(stmt);
7677
+ async function executeSimpleSelect(stmt, client, options, cacheContext, orderPlan, orderMeta) {
7678
+ const restStmt = orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt;
7679
+ const params = selectToKintoneParams(restStmt);
7680
+ const fetchFields = orderPlan?.kind === "CANONICAL_LOCAL" ? selectToFetchAllFields(stmt, stmt.from) : params.fields;
6730
7681
  const typedInFieldTypes = await loadTypedInFieldTypes(stmt, client, cacheContext);
6731
7682
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
7683
+ const projectionSemanticsResolver = stmt.columns.some((column) => column.type === "CASE_COL") ? await buildWhereFieldSemanticsResolver(
7684
+ stmt,
7685
+ client,
7686
+ cacheContext,
7687
+ void 0,
7688
+ selectCaseConditionsNeedFieldMetadata(stmt)
7689
+ ) : void 0;
6732
7690
  const maxRecords = options.maxRecords ?? 1e4;
6733
7691
  const warnings = /* @__PURE__ */ new Set();
6734
7692
  const onLimit = options.onLimitReached ?? "error";
6735
7693
  const parallel = options.fetchParallel ?? 1;
6736
- const useSingleGet = stmt.limit !== null && stmt.limit <= 500;
7694
+ const useRestWindow = stmt.orderBy.length > 0 ? orderPlan?.kind === "CANONICAL_REST_TOP_N" || orderPlan?.kind === "KORDER_NATIVE" : stmt.limit !== null && stmt.limit <= 500;
6737
7695
  const needed = stmt.limit === null ? null : (stmt.offset ?? 0) + stmt.limit;
6738
7696
  const stopAfter = stmt.orderBy.length === 0 && needed !== null && needed <= maxRecords && !whereHasKlike(stmt.where) ? needed : void 0;
6739
7697
  let records;
6740
- if (useSingleGet) {
7698
+ if (orderPlan?.kind === "KORDER_NATIVE" && stmt.limit === 0) {
7699
+ records = [];
7700
+ } else if (useRestWindow) {
6741
7701
  const res = await client.getRecords({
6742
7702
  app: params.app,
6743
7703
  query: params.query,
@@ -6750,7 +7710,7 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
6750
7710
  client.getRecords,
6751
7711
  params.app,
6752
7712
  baseQuery,
6753
- params.fields,
7713
+ fetchFields,
6754
7714
  {
6755
7715
  parallel,
6756
7716
  maxRecords,
@@ -6763,16 +7723,23 @@ async function executeSimpleSelect(stmt, client, options, cacheContext) {
6763
7723
  );
6764
7724
  }
6765
7725
  let rows = records.map((r) => flatten(r, null));
6766
- if (!useSingleGet) {
6767
- const { optionOrders, sortKinds } = await buildOrderByMetaForSelect(stmt, client, cacheContext);
6768
- rows = applyOrderBy(rows, stmt.orderBy, optionOrders, sortKinds);
7726
+ if (!useRestWindow) {
7727
+ rows = applyOrderBy(
7728
+ rows,
7729
+ stmt.orderBy,
7730
+ orderMeta.optionOrders,
7731
+ orderMeta.sortKinds,
7732
+ orderMeta.semantics
7733
+ );
6769
7734
  rows = applyLimit(rows, stmt.limit, stmt.offset);
6770
7735
  }
6771
7736
  const { rows: projected, columns } = project(
6772
7737
  rows,
6773
7738
  stmt.columns,
6774
7739
  void 0,
6775
- fieldTypeResolvers.row
7740
+ fieldTypeResolvers.row,
7741
+ void 0,
7742
+ projectionSemanticsResolver
6776
7743
  );
6777
7744
  return { type: "SELECT", rows: projected, columns, rowCount: projected.length, warnings: [...warnings] };
6778
7745
  }
@@ -6845,8 +7812,8 @@ async function loadTypedPushdownMeta(stmt, client, cacheContext) {
6845
7812
  const statusFields = collectCandidateFieldCodes(candidates).filter((fieldCode) => fieldTypes.get(fieldCode) === "STATUS");
6846
7813
  if (statusFields.length > 0) {
6847
7814
  const process2 = await getProcessStatusesCached(appId, client, cacheContext);
6848
- if (process2.enable && process2.states.length > 0) {
6849
- const states = new Set(process2.states);
7815
+ if (process2.enable && process2.states && process2.states.length > 0) {
7816
+ const states = new Set(process2.states.map((state) => state.name));
6850
7817
  for (const fieldCode of statusFields) fieldOptions.set(fieldCode, states);
6851
7818
  }
6852
7819
  }
@@ -7024,7 +7991,18 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
7024
7991
  return [appId, new Map(infos.map((info) => [info.code, info]))];
7025
7992
  }))
7026
7993
  );
7994
+ const statusOrdersByApp = /* @__PURE__ */ new Map();
7995
+ const aggregateFieldNames = new Set(refs.map((ref) => ref.field));
7996
+ await Promise.all([...fieldInfosByApp].map(async ([appId, infos]) => {
7997
+ if (![...aggregateFieldNames].some((field) => infos.get(field)?.fieldType === "STATUS")) return;
7998
+ const order = await loadProcessStatusOrder(appId, client, cacheContext);
7999
+ if (order) statusOrdersByApp.set(appId, order);
8000
+ }));
7027
8001
  const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
8002
+ const semanticsForInfo = (info, appId) => {
8003
+ const base = info.semantics ?? resolveFieldSemantics(info);
8004
+ return info.fieldType === "STATUS" && statusOrdersByApp.has(appId) ? { ...base, optionOrder: statusOrdersByApp.get(appId) } : base;
8005
+ };
7028
8006
  return (ref) => {
7029
8007
  let info;
7030
8008
  if (ref.tableAlias !== null) {
@@ -7034,40 +8012,130 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
7034
8012
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
7035
8013
  if (!table) return void 0;
7036
8014
  if (table.cteName !== null) {
7037
- return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.sortKind;
8015
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
7038
8016
  }
7039
8017
  info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
7040
8018
  }
7041
8019
  } else if (stmt.joins.length === 0) {
7042
8020
  if (stmt.from.cteName !== null) {
7043
- return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.sortKind;
8021
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string");
7044
8022
  }
7045
8023
  info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
7046
8024
  } else {
7047
8025
  const matches = tables.flatMap((table) => {
7048
8026
  if (table.cteName !== null) {
7049
8027
  const materialized = materializedTables?.get(table.cteName);
7050
- return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.sortKind] : [];
8028
+ return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.semantics ?? syntheticSemantics("string")] : [];
7051
8029
  }
7052
8030
  const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
7053
- return candidate ? [aggregateSortKind(candidate)] : [];
8031
+ return candidate ? [semanticsForInfo(candidate, table.appId)] : [];
7054
8032
  });
7055
8033
  if (matches.length !== 1) return void 0;
7056
8034
  return matches[0];
7057
8035
  }
7058
- return info ? aggregateSortKind(info) : void 0;
8036
+ if (!info) return void 0;
8037
+ const sourceTable = ref.tableAlias !== null ? tables.find((table) => table.alias === ref.tableAlias) : stmt.joins.length === 0 ? stmt.from : void 0;
8038
+ return semanticsForInfo(info, sourceTable?.appId ?? stmt.from.appId);
7059
8039
  };
7060
8040
  }
7061
8041
  function fieldCodeForTypeLookup(table, field) {
7062
8042
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
7063
8043
  return field;
7064
8044
  }
7065
- function materializedMetaFromFieldInfo(info) {
7066
- return { sortKind: aggregateSortKind(info), fieldType: info.fieldType };
8045
+ function materializedMetaFromFieldInfo(info, sourceAppId) {
8046
+ const semantics = info.semantics ?? resolveFieldSemantics(info);
8047
+ return {
8048
+ sortKind: aggregateSortKind(info),
8049
+ fieldType: info.fieldType,
8050
+ semantics: sourceAppId === void 0 ? semantics : withFieldSemanticSource(semantics, sourceAppId, info.code)
8051
+ };
8052
+ }
8053
+ function withCanonicalRestTie(stmt) {
8054
+ const hasId = stmt.orderBy.some(
8055
+ (item) => item.key.type === "FIELD_NAME" && item.key.name === "$id"
8056
+ );
8057
+ return hasId ? stmt : {
8058
+ ...stmt,
8059
+ orderBy: [...stmt.orderBy, { key: { type: "FIELD_NAME", name: "$id" }, direction: "ASC" }]
8060
+ };
8061
+ }
8062
+ function syntheticColumnMeta(compareMode) {
8063
+ return { sortKind: compareMode, semantics: syntheticSemantics(compareMode) };
8064
+ }
8065
+ function unknownStringColumnMeta() {
8066
+ return { semantics: syntheticSemantics("string", "KSQL_UNKNOWN") };
8067
+ }
8068
+ function unsupportedColumnMeta(fieldType = "KSQL_ARRAY") {
8069
+ return {
8070
+ semantics: { fieldType, compareMode: "unsupported", inSubtable: false, requiresCollectionOperators: false }
8071
+ };
8072
+ }
8073
+ function systemColumnMeta(field) {
8074
+ if (field === "$id" || field === "_rid" || field === "_pid") {
8075
+ return {
8076
+ sortKind: "number",
8077
+ fieldType: "__ID__",
8078
+ semantics: resolveFieldSemantics({ fieldType: "__ID__" })
8079
+ };
8080
+ }
8081
+ if (field === "$revision") return syntheticColumnMeta("number");
8082
+ return void 0;
8083
+ }
8084
+ var NUMBER_RETURNING_STRING_FUNCTIONS = /* @__PURE__ */ new Set([
8085
+ "LENGTH",
8086
+ "INSTR",
8087
+ "ROUND",
8088
+ "FLOOR",
8089
+ "CEIL",
8090
+ "TRUNCATE",
8091
+ "YEAR",
8092
+ "MONTH",
8093
+ "DAY",
8094
+ "DATEDIFF",
8095
+ "ABS",
8096
+ "MOD",
8097
+ "POWER",
8098
+ "SQRT"
8099
+ ]);
8100
+ function stringFunctionColumnMeta(expr) {
8101
+ if (expr.func === "CAST") {
8102
+ const target = expr.args[1];
8103
+ return target?.type === "STRING" && target.value === "NUMBER" ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
8104
+ }
8105
+ return NUMBER_RETURNING_STRING_FUNCTIONS.has(expr.func) ? syntheticColumnMeta("number") : syntheticColumnMeta("string");
8106
+ }
8107
+ function caseResultColumnMeta(result, resolveField2) {
8108
+ if (result.type === "STRING") return syntheticColumnMeta("string");
8109
+ if (result.type === "ARRAY") return unsupportedColumnMeta();
8110
+ if (result.type === "NUMBER" || result.type === "ARITH") return syntheticColumnMeta("number");
8111
+ if (result.type === "STRING_FUNC") return stringFunctionColumnMeta(result);
8112
+ const source = resolveField2(aggregateFieldRef(result.field));
8113
+ return source ?? unknownStringColumnMeta();
8114
+ }
8115
+ function mergeExpressionColumnMeta(candidates) {
8116
+ if (candidates.length === 0) return unknownStringColumnMeta();
8117
+ const first = candidates[0];
8118
+ const withoutSource = (semantics) => {
8119
+ if (!semantics) return void 0;
8120
+ const { source: _source, ...rest } = semantics;
8121
+ return rest;
8122
+ };
8123
+ if (candidates.every(
8124
+ (candidate) => candidate.sortKind === first.sortKind && candidate.fieldType === first.fieldType && fieldSemanticsEqual(withoutSource(candidate.semantics), withoutSource(first.semantics))
8125
+ )) {
8126
+ const sameSource = candidates.every(
8127
+ (candidate) => fieldSemanticsEqual(candidate.semantics, first.semantics)
8128
+ );
8129
+ return sameSource ? first : { ...first, semantics: withoutSource(first.semantics) };
8130
+ }
8131
+ if (candidates.some((candidate) => candidate.semantics?.compareMode === "unsupported")) {
8132
+ return unsupportedColumnMeta("KSQL_MIXED_UNSUPPORTED");
8133
+ }
8134
+ return unknownStringColumnMeta();
7067
8135
  }
7068
8136
  function selectNeedsSourceColumnMeta(stmt) {
7069
8137
  return stmt.columns.some(
7070
- (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"
8138
+ (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"
7071
8139
  );
7072
8140
  }
7073
8141
  async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
@@ -7084,18 +8152,18 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
7084
8152
  if (ref.tableAlias !== null) {
7085
8153
  if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
7086
8154
  const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
7087
- return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
8155
+ return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
7088
8156
  }
7089
8157
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
7090
8158
  if (!table) return void 0;
7091
8159
  if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
7092
8160
  const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
7093
- return info ? materializedMetaFromFieldInfo(info) : void 0;
8161
+ return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
7094
8162
  }
7095
8163
  if (stmt.joins.length === 0) {
7096
8164
  if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
7097
8165
  const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
7098
- return info ? materializedMetaFromFieldInfo(info) : void 0;
8166
+ return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
7099
8167
  }
7100
8168
  const matches = tables.flatMap((table) => {
7101
8169
  if (table.cteName !== null) {
@@ -7104,7 +8172,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
7104
8172
  return [materialized.columnMeta?.get(ref.field)];
7105
8173
  }
7106
8174
  const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
7107
- return info ? [materializedMetaFromFieldInfo(info)] : [];
8175
+ const meta = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
8176
+ return meta ? [meta] : [];
7108
8177
  });
7109
8178
  return matches.length === 1 ? matches[0] : void 0;
7110
8179
  };
@@ -7134,19 +8203,27 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
7134
8203
  meta = resolveField2(aggregateFieldRef(column.field));
7135
8204
  } else if (column.type === "AGGREGATE") {
7136
8205
  if (column.func === "GROUP_CONCAT") {
7137
- meta = { sortKind: "string" };
8206
+ meta = syntheticColumnMeta("string");
7138
8207
  } else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
7139
- meta = { sortKind: "number" };
8208
+ meta = syntheticColumnMeta("number");
7140
8209
  } else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
7141
8210
  const source = resolveField2(aggregateFieldRef(column.arg.field));
7142
- if (source?.sortKind) meta = { sortKind: source.sortKind };
8211
+ if (source) meta = source;
7143
8212
  }
7144
8213
  } else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
7145
- meta = { sortKind: "number" };
8214
+ meta = syntheticColumnMeta("number");
7146
8215
  } else if (column.type === "LITERAL_COL") {
7147
- meta = { sortKind: "string" };
8216
+ meta = syntheticColumnMeta("string");
8217
+ } else if (column.type === "STRFUNC_COL") {
8218
+ meta = stringFunctionColumnMeta(column.expr);
7148
8219
  } else if (column.type === "WINDOW_COL") {
7149
- meta = { sortKind: "number" };
8220
+ meta = syntheticColumnMeta("number");
8221
+ } else if (column.type === "CASE_COL") {
8222
+ const results = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
8223
+ if (column.expr.elseResult) results.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
8224
+ meta = mergeExpressionColumnMeta(results);
8225
+ } else if (column.type === "SCALAR_SUBQUERY_COL") {
8226
+ meta = unknownStringColumnMeta();
7150
8227
  }
7151
8228
  if (meta) inferred.set(output, meta);
7152
8229
  });
@@ -7160,7 +8237,8 @@ function mergeUnionColumnMeta(left, right) {
7160
8237
  const a = leftMeta?.get(column);
7161
8238
  const rightColumn = right.columns[index];
7162
8239
  const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
7163
- if (a && b && a.sortKind === b.sortKind && a.fieldType === b.fieldType) merged.set(column, a);
8240
+ if (a && b) merged.set(column, mergeExpressionColumnMeta([a, b]));
8241
+ else if (a || b) merged.set(column, unknownStringColumnMeta());
7164
8242
  });
7165
8243
  return merged;
7166
8244
  }
@@ -7197,7 +8275,7 @@ function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
7197
8275
  };
7198
8276
  return { row, having };
7199
8277
  }
7200
- async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache) {
8278
+ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCache, allowOriginalWherePushdown = true, preloadedOrderMeta) {
7201
8279
  const maxRecords = options.maxRecords ?? 1e4;
7202
8280
  const warnings = /* @__PURE__ */ new Set();
7203
8281
  const parallel = options.fetchParallel ?? 1;
@@ -7211,6 +8289,14 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
7211
8289
  loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
7212
8290
  ]);
7213
8291
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
8292
+ const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
8293
+ stmt,
8294
+ client,
8295
+ cacheContext,
8296
+ cteCache,
8297
+ whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
8298
+ );
8299
+ const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
7214
8300
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
7215
8301
  validateKlikePushdownPlan(pushdownPlan);
7216
8302
  const mainPushDown = pushdownPlan.mainCondition;
@@ -7224,7 +8310,8 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
7224
8310
  true,
7225
8311
  options.onLimitReached ?? "error",
7226
8312
  warnings,
7227
- mainPushDown
8313
+ mainPushDown,
8314
+ allowOriginalWherePushdown
7228
8315
  );
7229
8316
  const parallelJoins = [];
7230
8317
  const onOptJoins = [];
@@ -7250,7 +8337,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
7250
8337
  }
7251
8338
  }
7252
8339
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
7253
- const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
8340
+ const orderByMetaPromise = preloadedOrderMeta ? Promise.resolve(preloadedOrderMeta) : buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
7254
8341
  scalarCachePromise.catch(() => {
7255
8342
  });
7256
8343
  orderByMetaPromise.catch(() => {
@@ -7287,15 +8374,18 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
7287
8374
  tables.set(join2.table.alias, joinRecords);
7288
8375
  }));
7289
8376
  const scalarCache = await scalarCachePromise;
7290
- const { optionOrders, sortKinds } = await orderByMetaPromise;
8377
+ const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
7291
8378
  const { rows, columns } = runFullScan({
7292
8379
  tables,
7293
8380
  stmt,
7294
8381
  scalarCache,
7295
8382
  optionOrders,
7296
8383
  sortKinds,
8384
+ orderSemantics: semantics,
7297
8385
  fieldTypeResolver: fieldTypeResolvers.row,
8386
+ fieldSemanticsResolver,
7298
8387
  havingFieldTypeResolver: fieldTypeResolvers.having,
8388
+ havingFieldSemanticsResolver,
7299
8389
  aggregateSortKindResolver,
7300
8390
  appliedKlikes: pushdownPlan.appliedKlikes
7301
8391
  });
@@ -7392,16 +8482,39 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
7392
8482
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
7393
8483
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
7394
8484
  ]);
8485
+ const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
8486
+ if (whereCapability.capability === "UNSUPPORTED") {
8487
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
8488
+ }
8489
+ const orderMeta = await buildOrderByMetaForSelect(stmt, client, cacheContext, cteCache);
8490
+ if (hasCanonicalOrder(stmt)) {
8491
+ (stmt.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
8492
+ stmt,
8493
+ staticMode: "FULL_SCAN",
8494
+ whereCapability: whereCapability.capability,
8495
+ orderSemantics: orderMeta.semantics,
8496
+ maxRecords,
8497
+ hasKlike: whereHasKlike(stmt.where)
8498
+ });
8499
+ }
7395
8500
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
7396
8501
  loadTypedPushdownMeta(stmt, client, cacheContext),
7397
8502
  loadTypedInFieldTypes(stmt, client, cacheContext),
7398
8503
  loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
7399
8504
  ]);
7400
8505
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
8506
+ const fieldSemanticsResolver = await buildWhereFieldSemanticsResolver(
8507
+ stmt,
8508
+ client,
8509
+ cacheContext,
8510
+ cteCache,
8511
+ whereNeedsFieldMetadata(stmt.having) || selectCaseConditionsNeedFieldMetadata(stmt)
8512
+ );
8513
+ const havingFieldSemanticsResolver = buildHavingFieldSemanticsResolver(stmt, fieldSemanticsResolver);
7401
8514
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
7402
8515
  validateKlikePushdownPlan(pushdownPlan);
7403
8516
  const scalarCachePromise = resolveScalarColumns(stmt.columns, client, options, cacheContext, cteCache);
7404
- const orderByMetaPromise = buildOrderByMetaForSelect(stmt, client, cacheContext);
8517
+ const orderByMetaPromise = Promise.resolve(orderMeta);
7405
8518
  scalarCachePromise.catch(() => {
7406
8519
  });
7407
8520
  orderByMetaPromise.catch(() => {
@@ -7420,7 +8533,8 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
7420
8533
  true,
7421
8534
  options.onLimitReached ?? "error",
7422
8535
  warnings,
7423
- pushdownPlan.mainCondition
8536
+ pushdownPlan.mainCondition,
8537
+ whereCapability.capability === "EXACT_PUSHDOWN"
7424
8538
  );
7425
8539
  tables.set(stmt.from.alias, mainRecords);
7426
8540
  }
@@ -7457,7 +8571,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
7457
8571
  });
7458
8572
  await Promise.all(joinFetches);
7459
8573
  const scalarCache = await scalarCachePromise;
7460
- const { optionOrders, sortKinds } = await orderByMetaPromise;
8574
+ const { optionOrders, sortKinds, semantics } = await orderByMetaPromise;
7461
8575
  const sourceColumns = stmt.joins.length === 0 && stmt.from.cteName != null ? cteCache.get(stmt.from.cteName)?.columns : void 0;
7462
8576
  const { rows, columns } = runFullScan({
7463
8577
  tables,
@@ -7465,8 +8579,11 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
7465
8579
  scalarCache,
7466
8580
  optionOrders,
7467
8581
  sortKinds,
8582
+ orderSemantics: semantics,
7468
8583
  fieldTypeResolver: fieldTypeResolvers.row,
8584
+ fieldSemanticsResolver,
7469
8585
  havingFieldTypeResolver: fieldTypeResolvers.having,
8586
+ havingFieldSemanticsResolver,
7470
8587
  aggregateSortKindResolver,
7471
8588
  appliedKlikes: pushdownPlan.appliedKlikes,
7472
8589
  sourceColumns
@@ -7478,13 +8595,13 @@ function processRowToKintoneRecord(row) {
7478
8595
  Object.entries(row).map(([k, v]) => [k, { value: v ?? "" }])
7479
8596
  );
7480
8597
  }
7481
- async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null) {
8598
+ async function fetchTableRecordsForFullScan(stmt, table, client, maxRecords, parallel, isMainTable, onLimit, warnings, pushDownCond = null, allowOriginalWherePushdown = true) {
7482
8599
  const fields = selectToFetchAllFields(stmt, table);
7483
8600
  const onTruncate = (max) => {
7484
8601
  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`);
7485
8602
  };
7486
8603
  if (!table.subtableCode) {
7487
- const baseQuery = isMainTable ? selectToFetchAllParams(stmt, table.appId).query : "";
8604
+ const baseQuery = isMainTable && allowOriginalWherePushdown ? selectToFetchAllParams(stmt, table.appId).query : "";
7488
8605
  const pushQuery = pushDownCond !== null ? whereToKintone(pushDownCond) : "";
7489
8606
  const query = baseQuery && pushQuery ? `(${baseQuery}) and (${pushQuery})` : baseQuery || pushQuery;
7490
8607
  const resolved = await fetchRecordsForSharedPlan(client.getRecords, table.appId, query, fields, {
@@ -7688,6 +8805,10 @@ async function getProcessStatusesCached(appId, client, cacheContext) {
7688
8805
  setScopedCacheValue(processStatusCache, cacheContext, appId, loading);
7689
8806
  return loading;
7690
8807
  }
8808
+ async function loadProcessStatusOrder(appId, client, cacheContext) {
8809
+ const process2 = await getProcessStatusesCached(appId, client, cacheContext);
8810
+ return process2.enable && process2.states !== null ? new Map(process2.states.map((state) => [state.name, state.index])) : void 0;
8811
+ }
7691
8812
  async function getFieldTypeMap(appId, client, cacheContext) {
7692
8813
  const cached = getScopedCacheValue(fieldTypeCache, cacheContext, appId);
7693
8814
  if (cached) return cached;
@@ -7731,18 +8852,118 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
7731
8852
  setScopedCacheValue(sortKindCache, cacheContext, appId, map);
7732
8853
  return map;
7733
8854
  }
7734
- async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
8855
+ function orderByFieldNames(stmt) {
8856
+ const items = [
8857
+ ...stmt.orderBy,
8858
+ ...stmt.columns.flatMap((column) => column.type === "WINDOW_COL" ? column.orderBy : [])
8859
+ ];
8860
+ return [...new Set(items.flatMap(
8861
+ (item) => item.key.type === "FIELD_NAME" ? [item.key.name] : []
8862
+ ))];
8863
+ }
8864
+ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables) {
8865
+ const names = orderByFieldNames(stmt);
8866
+ if (names.length === 0) return /* @__PURE__ */ new Map();
8867
+ const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
8868
+ const ambiguousFields = /* @__PURE__ */ new Set();
8869
+ const infosByApp = new Map(
8870
+ await Promise.all([...new Set(
8871
+ tables.filter((table) => table.cteName === null).map((table) => table.appId)
8872
+ )].map(async (appId) => {
8873
+ const infos = await getFieldsCached(appId, client, cacheContext);
8874
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
8875
+ }))
8876
+ );
8877
+ const resolveField2 = (ref) => {
8878
+ if (ref.tableAlias !== null) {
8879
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
8880
+ const info2 = infosByApp.get(stmt.from.appId)?.get(ref.field);
8881
+ return info2 ? materializedMetaFromFieldInfo(info2, stmt.from.appId) : void 0;
8882
+ }
8883
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
8884
+ if (!table) return void 0;
8885
+ if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
8886
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
8887
+ return info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
8888
+ }
8889
+ if (stmt.joins.length === 0) {
8890
+ if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
8891
+ const info = infosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
8892
+ return info ? materializedMetaFromFieldInfo(info, stmt.from.appId) : systemColumnMeta(ref.field);
8893
+ }
8894
+ const matches = tables.flatMap((table) => {
8895
+ if (table.cteName !== null) {
8896
+ const materialized = materializedTables?.get(table.cteName);
8897
+ const meta2 = materialized?.columns.includes(ref.field) ? materialized.columnMeta?.get(ref.field) : void 0;
8898
+ return meta2 ? [meta2] : [];
8899
+ }
8900
+ const info = infosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
8901
+ const meta = info ? materializedMetaFromFieldInfo(info, table.appId) : systemColumnMeta(ref.field);
8902
+ return meta ? [meta] : [];
8903
+ });
8904
+ if (matches.length > 1) ambiguousFields.add(ref.field);
8905
+ return matches.length === 1 ? matches[0] : void 0;
8906
+ };
8907
+ const aliasSemantics = /* @__PURE__ */ new Map();
8908
+ for (const column of stmt.columns) {
8909
+ if (!("alias" in column) || !column.alias) continue;
8910
+ let meta;
8911
+ if (column.type === "FIELD") meta = resolveField2(aggregateFieldRef(column.field));
8912
+ else if (column.type === "ARITH_COL" || column.type === "ARITH_AGG_COL" || column.type === "WINDOW_COL") {
8913
+ meta = syntheticColumnMeta("number");
8914
+ } else if (column.type === "LITERAL_COL") meta = syntheticColumnMeta("string");
8915
+ else if (column.type === "STRFUNC_COL") meta = stringFunctionColumnMeta(column.expr);
8916
+ else if (column.type === "SCALAR_SUBQUERY_COL") meta = unknownStringColumnMeta();
8917
+ else if (column.type === "CASE_COL") {
8918
+ const candidates = column.expr.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
8919
+ if (column.expr.elseResult) candidates.push(caseResultColumnMeta(column.expr.elseResult, resolveField2));
8920
+ meta = mergeExpressionColumnMeta(candidates);
8921
+ } else if (column.type === "AGGREGATE") {
8922
+ if (column.func === "MIN" || column.func === "MAX") {
8923
+ meta = column.arg.type === "FIELD_REF" ? resolveField2(aggregateFieldRef(column.arg.field)) : syntheticColumnMeta("number");
8924
+ } else {
8925
+ meta = column.func === "GROUP_CONCAT" ? syntheticColumnMeta("string") : syntheticColumnMeta("number");
8926
+ }
8927
+ }
8928
+ if (meta?.semantics) aliasSemantics.set(column.alias, meta.semantics);
8929
+ }
8930
+ const result = /* @__PURE__ */ new Map();
8931
+ for (const name of names) {
8932
+ const base = aliasSemantics.get(name) ?? resolveField2(aggregateFieldRef(name))?.semantics;
8933
+ if (!base) {
8934
+ const ref = aggregateFieldRef(name);
8935
+ if (ref.tableAlias === null && ambiguousFields.has(ref.field)) {
8936
+ result.set(name, resolveFieldSemantics({ fieldType: "KSQL_AMBIGUOUS" }));
8937
+ }
8938
+ continue;
8939
+ }
8940
+ let semantics = base;
8941
+ if (base.fieldType === "STATUS" && base.source && stmt.orderMode !== "KINTONE_NATIVE") {
8942
+ const process2 = await getProcessStatusesCached(base.source.appId, client, cacheContext);
8943
+ if (process2.enable && process2.states !== null) {
8944
+ semantics = {
8945
+ ...base,
8946
+ optionOrder: new Map(process2.states.map((state) => [state.name, state.index]))
8947
+ };
8948
+ }
8949
+ }
8950
+ result.set(name, semantics);
8951
+ }
8952
+ return result;
8953
+ }
8954
+ async function buildOrderByMetaForSelect(stmt, client, cacheContext, materializedTables) {
7735
8955
  const hasWindowOrderBy = stmt.columns.some(
7736
8956
  (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
7737
8957
  );
7738
8958
  if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
7739
- return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
8959
+ return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map(), semantics: /* @__PURE__ */ new Map() };
7740
8960
  }
7741
- const [optionOrders, sortKinds] = await Promise.all([
8961
+ const [optionOrders, sortKinds, semantics] = await Promise.all([
7742
8962
  buildOptionOrdersForSelect(stmt, client, cacheContext),
7743
- buildSortKindsForSelect(stmt, client, cacheContext)
8963
+ buildSortKindsForSelect(stmt, client, cacheContext),
8964
+ buildOrderSemanticsForSelect(stmt, client, cacheContext, materializedTables)
7744
8965
  ]);
7745
- return { optionOrders, sortKinds };
8966
+ return { optionOrders, sortKinds, semantics };
7746
8967
  }
7747
8968
  async function buildOptionOrdersForSelect(stmt, client, cacheContext) {
7748
8969
  const optionOrders = /* @__PURE__ */ new Map();
@@ -7840,6 +9061,9 @@ var RejectLimitExceededError = class extends Error {
7840
9061
  }
7841
9062
  };
7842
9063
  async function prepareDmlValidation(stmt, client, options, cacheContext, tempTables, statementNumber) {
9064
+ if (stmt.type === "UPDATE") {
9065
+ await assertDmlWhereCapability(stmt, client, cacheContext);
9066
+ }
7843
9067
  const operation = stmt.type === "UPDATE" ? "UPDATE" : stmt.type.startsWith("UPSERT") ? "UPSERT" : "INSERT";
7844
9068
  const payloadFields = stmt.type === "UPDATE" ? ["$id", ...stmt.assignments.map((a) => a.field)] : [...stmt.fields];
7845
9069
  if (new Set(payloadFields).size !== payloadFields.length) {
@@ -7879,18 +9103,22 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
7879
9103
  const columnMeta = /* @__PURE__ */ new Map();
7880
9104
  for (const column of payloadFields) {
7881
9105
  if (column === "$id") {
7882
- columnMeta.set(column, { sortKind: "number", fieldType: "RECORD_NUMBER" });
9106
+ columnMeta.set(column, {
9107
+ sortKind: "number",
9108
+ fieldType: "RECORD_NUMBER",
9109
+ semantics: resolveFieldSemantics({ fieldType: "RECORD_NUMBER" })
9110
+ });
7883
9111
  continue;
7884
9112
  }
7885
9113
  const info = infoByCode.get(column);
7886
- if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
7887
- }
7888
- columnMeta.set("$err_statement", { sortKind: "number" });
7889
- columnMeta.set("$err_operation", { sortKind: "string" });
7890
- columnMeta.set("$err_row", { sortKind: "number" });
7891
- columnMeta.set("$err_field", { sortKind: "string" });
7892
- columnMeta.set("$err_code", { sortKind: "string" });
7893
- columnMeta.set("$err_message", { sortKind: "string" });
9114
+ if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info, stmt.appId));
9115
+ }
9116
+ columnMeta.set("$err_statement", syntheticColumnMeta("number"));
9117
+ columnMeta.set("$err_operation", syntheticColumnMeta("string"));
9118
+ columnMeta.set("$err_row", syntheticColumnMeta("number"));
9119
+ columnMeta.set("$err_field", syntheticColumnMeta("string"));
9120
+ columnMeta.set("$err_code", syntheticColumnMeta("string"));
9121
+ columnMeta.set("$err_message", syntheticColumnMeta("string"));
7894
9122
  materializedMetaByValidationResult.set(result, columnMeta);
7895
9123
  return { result, candidates, invalidRowNumbers, columnMeta };
7896
9124
  }
@@ -8276,6 +9504,7 @@ async function executeInsertSelect(stmt, client, options, cacheContext, cteCache
8276
9504
  };
8277
9505
  }
8278
9506
  async function executeUpdate(stmt, client, options, cacheContext, tempTables) {
9507
+ await assertDmlWhereCapability(stmt, client, cacheContext);
8279
9508
  if (stmt.subtableCode) {
8280
9509
  return executeUpdateSubtable(stmt, client, options, cacheContext);
8281
9510
  }
@@ -8353,6 +9582,7 @@ function collectUpdateFromTargetFields(stmt) {
8353
9582
  return [...fields];
8354
9583
  }
8355
9584
  async function executeDelete(stmt, client, options, cacheContext) {
9585
+ await assertDmlWhereCapability(stmt, client, cacheContext);
8356
9586
  if (stmt.subtableCode) {
8357
9587
  return executeDeleteSubtable(stmt, client, options, cacheContext);
8358
9588
  }
@@ -8725,6 +9955,18 @@ async function executeReorder(stmt, client, options, cacheContext) {
8725
9955
  client,
8726
9956
  cacheContext
8727
9957
  );
9958
+ const reorderFields = await getFieldsCached(stmt.appId, client, cacheContext);
9959
+ const reorderSemanticsByCode = new Map(reorderFields.map((field) => [
9960
+ field.code,
9961
+ field.semantics ?? resolveFieldSemantics(field)
9962
+ ]));
9963
+ const resolveReorderSemantics = (field) => {
9964
+ if (field.field === "_idx" || field.field === "_pid" || field.field === "_rid") {
9965
+ return syntheticSemantics("number");
9966
+ }
9967
+ const code = field.field.startsWith("_p.") ? field.field.slice(3) : field.field;
9968
+ return reorderSemanticsByCode.get(code) ?? syntheticSemantics("string");
9969
+ };
8728
9970
  const parents = await fetchAll(
8729
9971
  client.getRecords,
8730
9972
  stmt.appId,
@@ -8733,7 +9975,13 @@ async function executeReorder(stmt, client, options, cacheContext) {
8733
9975
  { maxRecords: options.maxRecords ?? 1e4, parallel: options.fetchParallel ?? 1 }
8734
9976
  );
8735
9977
  const expanded = expandRowsForSubtableDml(parents, stmt.subtableCode);
8736
- 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));
9978
+ const targetParentIds = stmt.all ? new Set(parents.map((p) => String(p["$id"]?.value ?? "")).filter((id) => id !== "")) : new Set(expanded.filter((r) => stmt.where && evalWhere(
9979
+ stmt.where,
9980
+ r.flat,
9981
+ resolveFieldType,
9982
+ void 0,
9983
+ resolveReorderSemantics
9984
+ )).map((r) => r.parentId));
8737
9985
  if (options.confirm) {
8738
9986
  const ok = await options.confirm(targetParentIds.size, "UPDATE");
8739
9987
  if (!ok) throw new OperationCancelledError("UPDATE", targetParentIds.size);
@@ -8744,7 +9992,7 @@ async function executeReorder(stmt, client, options, cacheContext) {
8744
9992
  if (!parent) continue;
8745
9993
  const rows = getMutableTableRows(parent, stmt.subtableCode);
8746
9994
  const sortable = rows.map((row, i) => ({ row, i, flat: buildFlatRowForSort(parent, stmt.subtableCode, row, i) }));
8747
- sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by));
9995
+ sortable.sort((a, b) => compareByOrder(a.flat, b.flat, stmt.by, resolveReorderSemantics));
8748
9996
  const orderedRowIds = sortable.map((x) => x.row.id ?? "");
8749
9997
  await client.putRecords(buildSubtableReorderPutParams(stmt.appId, pid, getRevision(parent), stmt.subtableCode, orderedRowIds));
8750
9998
  }
@@ -8765,14 +10013,12 @@ function buildFlatRowForSort(parent, subtableCode, row, idx) {
8765
10013
  }
8766
10014
  return flat;
8767
10015
  }
8768
- function compareByOrder(a, b, orderBy) {
10016
+ function compareByOrder(a, b, orderBy, resolveSemantics) {
8769
10017
  for (const item of orderBy) {
8770
10018
  const av = evalOrderKeyForRow(item.key, a);
8771
10019
  const bv = evalOrderKeyForRow(item.key, b);
8772
- const an = Number(av);
8773
- const bn = Number(bv);
8774
- const numeric = !Number.isNaN(an) && !Number.isNaN(bn);
8775
- const cmp = numeric ? an - bn : av.localeCompare(bv, "ja");
10020
+ 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");
10021
+ const cmp = compareCanonicalValues(av, bv, semantics ?? syntheticSemantics("string"));
8776
10022
  if (cmp !== 0) return item.direction === "ASC" ? cmp : -cmp;
8777
10023
  }
8778
10024
  return 0;
@@ -8969,35 +10215,140 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
8969
10215
  pending.forEach(([i], idx) => cache.set(i, values[idx]));
8970
10216
  return cache;
8971
10217
  }
8972
- function buildBatchExplainPlans(sql, injectedVariables) {
10218
+ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4) {
10219
+ const fieldApps = /* @__PURE__ */ new Set();
10220
+ const processStatusApps = /* @__PURE__ */ new Set();
10221
+ const tracedClient = {
10222
+ ...client,
10223
+ getFields: async (appId) => {
10224
+ fieldApps.add(appId);
10225
+ return client.getFields(appId);
10226
+ },
10227
+ getProcessStatuses: async (appId) => {
10228
+ processStatusApps.add(appId);
10229
+ return client.getProcessStatuses(appId);
10230
+ }
10231
+ };
10232
+ const capabilities = /* @__PURE__ */ new Map();
10233
+ const orderPlans = /* @__PURE__ */ new Map();
10234
+ const seen = /* @__PURE__ */ new Set();
10235
+ const visit = async (node) => {
10236
+ if (node === null || typeof node !== "object") return;
10237
+ if (seen.has(node)) return;
10238
+ seen.add(node);
10239
+ if (Array.isArray(node)) {
10240
+ await Promise.all(node.map(visit));
10241
+ return;
10242
+ }
10243
+ const typed = node;
10244
+ if (typed["type"] === "SELECT") {
10245
+ const select = node;
10246
+ const physicalApps = [select.from, ...select.joins.map((join2) => join2.table)].filter((table) => table.cteName === null).map((table) => table.appId);
10247
+ const needsWhereSchema = whereNeedsFieldMetadata(select.where);
10248
+ if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
10249
+ physicalApps.forEach((appId) => fieldApps.add(appId));
10250
+ }
10251
+ const capability = await resolveSelectWhereCapability(select, tracedClient, cacheContext);
10252
+ if (capability.capability === "UNSUPPORTED") {
10253
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
10254
+ }
10255
+ capabilities.set(select, capability);
10256
+ if (select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
10257
+ const meta = await buildOrderByMetaForSelect(select, tracedClient, cacheContext);
10258
+ if (select.orderMode !== "KINTONE_NATIVE") {
10259
+ for (const semantics of meta.semantics.values()) {
10260
+ if (semantics.fieldType === "STATUS" && semantics.source) {
10261
+ processStatusApps.add(semantics.source.appId);
10262
+ }
10263
+ }
10264
+ }
10265
+ const hasUnmaterializedSource = [select.from, ...select.joins.map((join2) => join2.table)].some((table) => table.cteName !== null);
10266
+ if (hasCanonicalOrder(select) && !hasUnmaterializedSource) {
10267
+ const mode = capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(select) : "FULL_SCAN";
10268
+ orderPlans.set(select, (select.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
10269
+ stmt: select,
10270
+ staticMode: mode,
10271
+ whereCapability: capability.capability,
10272
+ orderSemantics: meta.semantics,
10273
+ maxRecords,
10274
+ hasKlike: whereHasKlike(select.where)
10275
+ }));
10276
+ }
10277
+ }
10278
+ } else if (typed["type"] === "UPDATE" || typed["type"] === "DELETE") {
10279
+ fieldApps.add(node.appId);
10280
+ await assertDmlWhereCapability(
10281
+ node,
10282
+ tracedClient,
10283
+ cacheContext
10284
+ );
10285
+ }
10286
+ await Promise.all(Object.values(typed).map(visit));
10287
+ };
10288
+ await visit(query);
10289
+ if (typeof query === "object" && query !== null && query.type === "WITH" && canInlineSingleCte(query)) {
10290
+ const inlined = buildInlinedQuery(query);
10291
+ const capability = await resolveSelectWhereCapability(inlined, tracedClient, cacheContext);
10292
+ if (capability.capability === "UNSUPPORTED") {
10293
+ throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
10294
+ }
10295
+ capabilities.set(inlined, capability);
10296
+ if (hasCanonicalOrder(inlined)) {
10297
+ const meta = await buildOrderByMetaForSelect(inlined, tracedClient, cacheContext);
10298
+ orderPlans.set(inlined, (inlined.orderMode === "KINTONE_NATIVE" ? planKorderNative : planCanonicalOrder)({
10299
+ stmt: inlined,
10300
+ staticMode: capability.capability === "EXACT_PUSHDOWN" ? resolveSelectMode(inlined) : "FULL_SCAN",
10301
+ whereCapability: capability.capability,
10302
+ orderSemantics: meta.semantics,
10303
+ maxRecords,
10304
+ hasKlike: whereHasKlike(inlined.where)
10305
+ }));
10306
+ }
10307
+ }
10308
+ return { capabilities, orderPlans, fieldApps, processStatusApps };
10309
+ }
10310
+ function explainMetadataLines(analysis) {
10311
+ return [
10312
+ ...[...analysis.fieldApps].sort((a, b) => a - b).map((appId) => ` metadata API: form definition APP${appId}`),
10313
+ ...[...analysis.processStatusApps].sort((a, b) => a - b).map((appId) => ` metadata API: process status APP${appId}`)
10314
+ ];
10315
+ }
10316
+ async function buildBatchExplainPlans(sql, client, injectedVariables, cacheContext = "batch-explain", maxRecords = 1e4) {
8973
10317
  const statements = parseSqlBatch(sql);
8974
10318
  const analysis = analyzeBatch(statements);
8975
10319
  validateDeclaredBatchVariables(statements, injectedVariables);
8976
10320
  const variables = /* @__PURE__ */ new Map();
8977
- return {
8978
- statementCount: statements.length,
8979
- statements: statements.map((stmt, i) => {
8980
- const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
8981
- validateKlikeStatement(planStmt);
8982
- const result = {
8983
- index: i,
8984
- type: analysis.statements[i].statementType,
8985
- plan: buildBatchStatementPlan(planStmt, analysis.statements[i])
8986
- };
8987
- if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
8988
- variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
8989
- }
8990
- return result;
8991
- })
8992
- };
10321
+ const plans = [];
10322
+ for (let i = 0; i < statements.length; i++) {
10323
+ const stmt = statements[i];
10324
+ const planStmt = stmt.type === "SET_VARIABLE" ? stmt.expr.type === "SCALAR_SUBQUERY" ? { ...stmt, expr: resolveVariableRefs(stmt.expr, variables) } : stmt : resolveVariableRefs(stmt, variables);
10325
+ validateKlikeStatement(planStmt);
10326
+ const whereAnalysis = await buildExplainWhereAnalysis(planStmt, client, cacheContext, maxRecords);
10327
+ const statementPlan = buildBatchStatementPlan(
10328
+ planStmt,
10329
+ analysis.statements[i],
10330
+ whereAnalysis.capabilities,
10331
+ whereAnalysis.orderPlans
10332
+ );
10333
+ const metadataPlan = explainMetadataLines(whereAnalysis);
10334
+ plans.push({
10335
+ index: i,
10336
+ type: analysis.statements[i].statementType,
10337
+ plan: statementPlan.length === 0 ? metadataPlan : [statementPlan[0], ...metadataPlan, ...statementPlan.slice(1)]
10338
+ });
10339
+ if (stmt.type === "SET_VARIABLE" || stmt.type === "DECLARE_VARIABLE") {
10340
+ variables.set(stmt.name, { type: "string", value: `@${stmt.name}` });
10341
+ }
10342
+ }
10343
+ return { statementCount: statements.length, statements: plans };
8993
10344
  }
8994
- function buildBatchStatementPlan(stmt, info) {
10345
+ function buildBatchStatementPlan(stmt, info, capabilities, orderPlans) {
8995
10346
  if (stmt.type === "CREATE_TEMP_TABLE") {
8996
10347
  return [
8997
10348
  `CREATE TEMP TABLE ${stmt.name}`,
8998
10349
  ` scope: batch\uFF08\u30D0\u30C3\u30C1\u7D42\u4E86\u6642\u306B\u81EA\u52D5\u7834\u68C4\uFF09`,
8999
10350
  ` 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`,
9000
- ...buildPlanForBatchQuery(stmt.query, info).map((l) => ` ${l}`)
10351
+ ...buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans).map((l) => ` ${l}`)
9001
10352
  ];
9002
10353
  }
9003
10354
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -9013,7 +10364,7 @@ function buildBatchStatementPlan(stmt, info) {
9013
10364
  `SET @${stmt.name} = (SELECT ...)`,
9014
10365
  " 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",
9015
10366
  " subquery:",
9016
- ...buildPlanForBatchQuery(stmt.expr.query, subInfo).map((l) => ` ${l}`)
10367
+ ...buildPlanForBatchQuery(stmt.expr.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`)
9017
10368
  ];
9018
10369
  }
9019
10370
  return [
@@ -9029,7 +10380,7 @@ function buildBatchStatementPlan(stmt, info) {
9029
10380
  }
9030
10381
  if (stmt.type === "SHOW_APPS") return ["SHOW APPS\uFF08\u30A2\u30D7\u30EA\u4E00\u89A7\u306E\u53D6\u5F97\uFF09"];
9031
10382
  if (stmt.type === "DESCRIBE") return [`DESCRIBE APP${stmt.appId}\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9\u5B9A\u7FA9\u306E\u53D6\u5F97\uFF09`];
9032
- if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info);
10383
+ if (stmt.type === "EXPLAIN") return buildPlanForBatchQuery(stmt.query, info, capabilities, orderPlans);
9033
10384
  if (stmt.type === "ASSERT") {
9034
10385
  const lines = [
9035
10386
  `ASSERT ${stmt.text}`,
@@ -9041,11 +10392,11 @@ function buildBatchStatementPlan(stmt, info) {
9041
10392
  subqueries.forEach((sq, i) => {
9042
10393
  lines.push(subqueries.length > 1 ? ` subquery[${i + 1}]:` : " subquery:");
9043
10394
  const subInfo = hasTempTableRef(sq.query) ? info : { ...info, tempTablesReferenced: [] };
9044
- lines.push(...buildPlanForBatchQuery(sq.query, subInfo).map((l) => ` ${l}`));
10395
+ lines.push(...buildPlanForBatchQuery(sq.query, subInfo, capabilities, orderPlans).map((l) => ` ${l}`));
9045
10396
  });
9046
10397
  return lines;
9047
10398
  }
9048
- return buildPlanForBatchQuery(stmt, info);
10399
+ return buildPlanForBatchQuery(stmt, info, capabilities, orderPlans);
9049
10400
  }
9050
10401
  function hasTempTableRef(node) {
9051
10402
  if (Array.isArray(node)) return node.some(hasTempTableRef);
@@ -9057,9 +10408,9 @@ function hasTempTableRef(node) {
9057
10408
  }
9058
10409
  return false;
9059
10410
  }
9060
- function buildPlanForBatchQuery(query, info) {
10411
+ function buildPlanForBatchQuery(query, info, capabilities, orderPlans) {
9061
10412
  if (info.tempTablesReferenced.length === 0) {
9062
- return buildExplainPlan(query);
10413
+ return buildExplainPlan(query, void 0, capabilities, orderPlans);
9063
10414
  }
9064
10415
  const lines = [];
9065
10416
  if (query.type === "INSERT_SELECT") {
@@ -9084,8 +10435,12 @@ function buildPlanForBatchQuery(query, info) {
9084
10435
  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");
9085
10436
  return lines;
9086
10437
  }
9087
- function executeExplain(stmt) {
9088
- const lines = buildExplainPlan(stmt.query);
10438
+ async function executeExplain(stmt, client, cacheContext, maxRecords) {
10439
+ const analysis = await buildExplainWhereAnalysis(stmt.query, client, cacheContext, maxRecords);
10440
+ const lines = [
10441
+ ...explainMetadataLines(analysis),
10442
+ ...buildExplainPlan(stmt.query, void 0, analysis.capabilities, analysis.orderPlans)
10443
+ ];
9089
10444
  return {
9090
10445
  type: "SELECT",
9091
10446
  columns: ["plan"],
@@ -9093,29 +10448,45 @@ function executeExplain(stmt) {
9093
10448
  rowCount: lines.length
9094
10449
  };
9095
10450
  }
9096
- function buildExplainPlan(query, label) {
9097
- if (query.type === "UNION") return buildUnionPlan(query);
9098
- if (query.type === "WITH") return buildWithPlan(query);
10451
+ function buildExplainPlan(query, label, capabilities, orderPlans) {
10452
+ if (query.type === "UNION") return buildUnionPlan(query, capabilities, orderPlans);
10453
+ if (query.type === "WITH") return buildWithPlan(query, capabilities, orderPlans);
9099
10454
  if (query.type === "INSERT") return buildInsertPlan(query, label);
9100
- if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label);
10455
+ if (query.type === "INSERT_SELECT") return buildInsertSelectPlan(query, label, capabilities, orderPlans);
9101
10456
  if (query.type === "UPSERT") return buildUpsertPlan(query, label);
9102
- if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label);
9103
- if (query.type === "UPDATE") return buildUpdatePlan(query, label);
10457
+ if (query.type === "UPSERT_SELECT") return buildUpsertSelectPlan(query, label, capabilities, orderPlans);
10458
+ if (query.type === "UPDATE") return buildUpdatePlan(query, label, capabilities, orderPlans);
9104
10459
  if (query.type === "DELETE") return buildDeletePlan(query, label);
9105
10460
  if (query.type === "REORDER") return buildReorderPlan(query, label);
9106
- return buildSelectPlan(query, label);
10461
+ return buildSelectPlan(query, label, capabilities, orderPlans);
9107
10462
  }
9108
- function buildSelectPlan(stmt, label) {
9109
- const mode = resolveSelectMode(stmt);
10463
+ function buildSelectPlan(stmt, label, capabilities, orderPlans) {
10464
+ const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
10465
+ const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
10466
+ const mode = orderPlan?.kind === "CANONICAL_LOCAL" ? "FULL_SCAN" : whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN" ? "FULL_SCAN" : resolveSelectMode(stmt);
9110
10467
  const reasons = collectFullScanReasons(stmt);
10468
+ if (whereCapability && whereCapability.capability !== "EXACT_PUSHDOWN") {
10469
+ reasons.push(...whereCapability.reasons.map((reason) => reason.code));
10470
+ }
9111
10471
  const lines = [];
9112
10472
  if (label) lines.push(label);
9113
10473
  lines.push(` mode: ${mode}`);
10474
+ if (orderPlan) {
10475
+ lines.push(` order plan: ${orderPlan.kind}`);
10476
+ if (orderPlan.reasonCodes.length > 0) lines.push(` order reason: ${orderPlan.reasonCodes.join(", ")}`);
10477
+ if (orderPlan.kind === "KORDER_NATIVE") {
10478
+ lines.push(" order semantics: kintone native (not kSQL canonical)");
10479
+ lines.push(" REST execution: single GET");
10480
+ }
10481
+ }
10482
+ if (orderPlan?.requiresCompleteInput ?? requiresCompleteInput(stmt)) {
10483
+ lines.push(" complete input: required (ORDER BY / window ORDER BY; onLimit=truncate disabled)");
10484
+ }
9114
10485
  if (mode === "FULL_SCAN" && reasons.length > 0) {
9115
10486
  lines.push(` reason: ${reasons.join(", ")}`);
9116
10487
  }
9117
10488
  if (mode === "SIMPLE") {
9118
- const params = selectToKintoneParams(stmt);
10489
+ const params = selectToKintoneParams(orderPlan?.kind === "CANONICAL_REST_TOP_N" ? withCanonicalRestTie(stmt) : stmt);
9119
10490
  lines.push(` app: APP${stmt.from.appId} (${stmt.from.appId})`);
9120
10491
  lines.push(` kintone query: ${params.query || "(\u306A\u3057)"}`);
9121
10492
  lines.push(` fields: ${params.fields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : params.fields.join(", ")}`);
@@ -9125,7 +10496,8 @@ function buildSelectPlan(stmt, label) {
9125
10496
  const mainAliasStr = stmt.from.alias ? ` AS ${stmt.from.alias}` : "";
9126
10497
  const mainPushDown = pushdownPlan.mainCondition;
9127
10498
  const mainCandidate = extractMainTypedPushdownCandidate(stmt);
9128
- const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : "(\u5168\u4EF6\u53D6\u5F97)";
10499
+ const exactOriginalWhere = stmt.joins.length === 0 && whereCapability?.capability === "EXACT_PUSHDOWN" && stmt.where !== null && !whereRequiresJsEval(stmt.where) ? whereToKintone(stmt.where) : "";
10500
+ const mainQ = mainPushDown !== null ? whereToKintone(mainPushDown) : exactOriginalWhere || "(\u5168\u4EF6\u53D6\u5F97)";
9129
10501
  lines.push(` app: APP${stmt.from.appId}${mainAliasStr} (${stmt.from.appId})`);
9130
10502
  lines.push(` kintone query: ${mainQ}`);
9131
10503
  if (mainCandidate !== null) {
@@ -9147,10 +10519,10 @@ function buildSelectPlan(stmt, label) {
9147
10519
  lines.push(` fields: ${joinFields.length === 0 ? "(\u5168\u30D5\u30A3\u30FC\u30EB\u30C9)" : joinFields.join(", ")}`);
9148
10520
  }
9149
10521
  }
9150
- lines.push(...collectSubqueryPlans(stmt));
10522
+ lines.push(...collectSubqueryPlans(stmt, capabilities, orderPlans));
9151
10523
  return lines;
9152
10524
  }
9153
- function buildUnionPlan(stmt) {
10525
+ function buildUnionPlan(stmt, capabilities, orderPlans) {
9154
10526
  const selects = [];
9155
10527
  const collect = (u) => {
9156
10528
  if (u.type === "SELECT") {
@@ -9164,24 +10536,25 @@ function buildUnionPlan(stmt) {
9164
10536
  const lines = [];
9165
10537
  selects.forEach((sel, i) => {
9166
10538
  if (i > 0) lines.push("");
9167
- lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`));
10539
+ lines.push(...buildSelectPlan(sel, `[union:${i + 1}]`, capabilities, orderPlans));
9168
10540
  });
9169
10541
  return lines;
9170
10542
  }
9171
- function buildWithPlan(stmt) {
10543
+ function buildWithPlan(stmt, capabilities, orderPlans) {
9172
10544
  const lines = [];
9173
10545
  for (const cte of stmt.ctes) {
9174
10546
  if (cte.query.type === "SELECT") {
9175
- lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`));
10547
+ lines.push(...buildSelectPlan(cte.query, `[cte: ${cte.name}]`, capabilities, orderPlans));
9176
10548
  lines.push("");
9177
10549
  }
9178
10550
  }
9179
10551
  if (stmt.query.type === "SELECT" || stmt.query.type === "UNION") {
9180
- lines.push(...buildExplainPlan(stmt.query, "[main]"));
10552
+ lines.push(...buildExplainPlan(stmt.query, "[main]", capabilities, orderPlans));
9181
10553
  }
9182
10554
  if (canInlineSingleCte(stmt)) {
9183
10555
  lines.push("");
9184
- lines.push(...buildSelectPlan(buildInlinedQuery(stmt), "[effective: inlined CTE]"));
10556
+ const inlined = buildInlinedQuery(stmt);
10557
+ lines.push(...buildSelectPlan(inlined, "[effective: inlined CTE]", capabilities, orderPlans));
9185
10558
  }
9186
10559
  return lines;
9187
10560
  }
@@ -9209,7 +10582,7 @@ function collectFullScanReasons(stmt) {
9209
10582
  r.push("ORDER BY \u306B\u5F0F");
9210
10583
  return r;
9211
10584
  }
9212
- function collectSubqueryPlans(stmt) {
10585
+ function collectSubqueryPlans(stmt, capabilities, orderPlans) {
9213
10586
  const lines = [];
9214
10587
  let idx = 1;
9215
10588
  const visitWhere = (w) => {
@@ -9218,16 +10591,16 @@ function collectSubqueryPlans(stmt) {
9218
10591
  case "BINARY":
9219
10592
  if (w.right.type === "SCALAR_SUBQUERY") {
9220
10593
  lines.push("");
9221
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`));
10594
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
9222
10595
  }
9223
10596
  if (w.right.type === "SUBQUERY_IN_LIST") {
9224
10597
  lines.push("");
9225
- lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`));
10598
+ lines.push(...buildSelectPlan(w.right.query, `[subquery:${idx++}]`, capabilities, orderPlans));
9226
10599
  }
9227
10600
  break;
9228
10601
  case "EXISTS":
9229
10602
  lines.push("");
9230
- lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`));
10603
+ lines.push(...buildSelectPlan(w.query, `[subquery:${idx++}]`, capabilities, orderPlans));
9231
10604
  break;
9232
10605
  case "LOGICAL":
9233
10606
  visitWhere(w.left);
@@ -9245,7 +10618,7 @@ function collectSubqueryPlans(stmt) {
9245
10618
  for (const col of stmt.columns) {
9246
10619
  if (col.type === "SCALAR_SUBQUERY_COL") {
9247
10620
  lines.push("");
9248
- lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`));
10621
+ lines.push(...buildSelectPlan(col.query, `[subquery:${idx++}]`, capabilities, orderPlans));
9249
10622
  }
9250
10623
  }
9251
10624
  if (stmt.having) visitWhere(stmt.having);
@@ -9263,7 +10636,7 @@ function buildInsertPlan(stmt, label) {
9263
10636
  lines.push(` fields: ${stmt.fields.join(", ")}`);
9264
10637
  return lines;
9265
10638
  }
9266
- function buildInsertSelectPlan(stmt, label) {
10639
+ function buildInsertSelectPlan(stmt, label, capabilities, orderPlans) {
9267
10640
  const lines = [];
9268
10641
  if (label) lines.push(label);
9269
10642
  lines.push(` [INSERT SELECT]`);
@@ -9271,10 +10644,10 @@ function buildInsertSelectPlan(stmt, label) {
9271
10644
  lines.push(` fields: ${stmt.fields.join(", ")}`);
9272
10645
  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`);
9273
10646
  lines.push("");
9274
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
10647
+ lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
9275
10648
  return lines;
9276
10649
  }
9277
- function buildUpdatePlan(stmt, label) {
10650
+ function buildUpdatePlan(stmt, label, capabilities, orderPlans) {
9278
10651
  const isArith = hasArithAssignment(stmt);
9279
10652
  const isSubq = stmt.assignments.some((a) => a.value.type === "SCALAR_SUBQUERY");
9280
10653
  const lines = [];
@@ -9308,7 +10681,7 @@ function buildUpdatePlan(stmt, label) {
9308
10681
  for (const a of stmt.assignments) {
9309
10682
  if (a.value.type === "SCALAR_SUBQUERY") {
9310
10683
  lines.push("");
9311
- lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`));
10684
+ lines.push(...buildSelectPlan(a.value.query, `[subquery: ${a.field}]`, capabilities, orderPlans));
9312
10685
  }
9313
10686
  }
9314
10687
  return lines;
@@ -9335,7 +10708,7 @@ function buildUpsertPlan(stmt, label) {
9335
10708
  ` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json \xD7 ${batchCount}`
9336
10709
  ];
9337
10710
  }
9338
- function buildUpsertSelectPlan(stmt, label) {
10711
+ function buildUpsertSelectPlan(stmt, label, capabilities, orderPlans) {
9339
10712
  const lines = [
9340
10713
  ...label ? [label] : [],
9341
10714
  ` [UPSERT SELECT]`,
@@ -9345,7 +10718,7 @@ function buildUpsertSelectPlan(stmt, label) {
9345
10718
  ` 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`,
9346
10719
  ``
9347
10720
  ];
9348
- lines.push(...buildSelectPlan(stmt.select, "[source SELECT]"));
10721
+ lines.push(...buildSelectPlan(stmt.select, "[source SELECT]", capabilities, orderPlans));
9349
10722
  return lines;
9350
10723
  }
9351
10724
  function buildReorderPlan(stmt, label) {
@@ -9887,12 +11260,14 @@ function flattenFormFieldProperties(properties) {
9887
11260
  function flattenFields(properties, lookupCopyFields, inSubtable = false) {
9888
11261
  const out = [];
9889
11262
  for (const field of Object.values(properties)) {
9890
- out.push({
11263
+ const optionOrder = toOptionOrderMap(field.options);
11264
+ const sortKind = detectSortKind(field.type, field.format);
11265
+ const info = {
9891
11266
  code: field.code,
9892
11267
  label: field.label,
9893
11268
  fieldType: field.type,
9894
- optionOrder: toOptionOrderMap(field.options),
9895
- sortKind: detectSortKind(field.type, field.format),
11269
+ optionOrder,
11270
+ sortKind,
9896
11271
  required: field.required,
9897
11272
  minValue: normalizeConstraintValue(field.minValue),
9898
11273
  maxValue: normalizeConstraintValue(field.maxValue),
@@ -9901,7 +11276,9 @@ function flattenFields(properties, lookupCopyFields, inSubtable = false) {
9901
11276
  defaultValue: field.defaultValue,
9902
11277
  inSubtable,
9903
11278
  writable: !lookupCopyFields.has(field.code) && !NON_WRITABLE_FIELD_TYPES2.has(field.type)
9904
- });
11279
+ };
11280
+ info.semantics = resolveFieldSemantics(info);
11281
+ out.push(info);
9905
11282
  if (field.fields) out.push(...flattenFields(field.fields, lookupCopyFields, true));
9906
11283
  }
9907
11284
  return out;
@@ -9956,6 +11333,18 @@ function detectSortKind(fieldType, calcFormat) {
9956
11333
  return void 0;
9957
11334
  }
9958
11335
 
11336
+ // src/core/processStatus.ts
11337
+ function normalizeProcessStatusStates(states) {
11338
+ if (states === null) return null;
11339
+ return Object.values(states).map((state) => {
11340
+ const index = Number(state.index);
11341
+ if (!Number.isSafeInteger(index) || index < 0) {
11342
+ throw new Error(`ArgumentError: invalid process status index: ${String(state.index)}`);
11343
+ }
11344
+ return { name: state.name, index };
11345
+ });
11346
+ }
11347
+
9959
11348
  // src/cli/nodeKintoneClient.ts
9960
11349
  var SEARCH_ABORTED_HEADER_VALUE = "Filter aborted because of too many search results";
9961
11350
  function createNodeKintoneClient(baseUrl, tokenResolver) {
@@ -10153,7 +11542,7 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
10153
11542
  const res = await requestJson(`${apiBasePath}/app/status.json?${qs.toString()}`, { method: "GET" }, appId);
10154
11543
  return {
10155
11544
  enable: res.enable,
10156
- states: Object.values(res.states ?? {}).map((state) => state.name)
11545
+ states: normalizeProcessStatusStates(res.states)
10157
11546
  };
10158
11547
  }
10159
11548
  };
@@ -10623,7 +12012,7 @@ Options:
10623
12012
  (batch + json: prints one JSON envelope for the whole batch)
10624
12013
  --max-records <n> Max records to fetch (default: 500)
10625
12014
  --fetch-parallel <n> Parallel page fetches per query: 1-10 (default: 3)
10626
- --on-limit <mode> On record limit: error | truncate
12015
+ --on-limit <mode> On record limit: error | truncate (local ORDER BY needs complete input)
10627
12016
  --temp-table-max-rows <n> Max rows per temp table (default: 10000, always errors on overflow)
10628
12017
  --timeout <ms> Request timeout in milliseconds (default: 30000)
10629
12018
  --max-concurrent <n> Max concurrent kintone requests: 1-50 (default: 10)
@@ -11907,7 +13296,7 @@ async function run() {
11907
13296
  let isBatchSql = false;
11908
13297
  let batchContainsDml = false;
11909
13298
  let batchAnalysis = null;
11910
- let needsCompleteInput = false;
13299
+ let dryRunNeedsMetadata = false;
11911
13300
  if (args.diagRecordId === null) {
11912
13301
  sql = args.executeSql;
11913
13302
  if (!sql && args.filePath) sql = (0, import_fs2.readFileSync)(args.filePath, "utf-8");
@@ -11934,17 +13323,16 @@ async function run() {
11934
13323
  }
11935
13324
  try {
11936
13325
  const statements = parseSqlStatements(sql);
13326
+ dryRunNeedsMetadata = statements.some(explainNeedsAppMetadata);
11937
13327
  if (statements.length > 1) {
11938
13328
  batchAnalysis = analyzeBatch(statements);
11939
13329
  isBatchSql = true;
11940
13330
  batchContainsDml = batchAnalysis.containsDml;
11941
- needsCompleteInput = batchAnalysis.requiresCompleteInput;
11942
13331
  } else {
11943
13332
  const stmt = parseSqlStatement(sql);
11944
13333
  parsedStmt = stmt;
11945
13334
  stmtType = getStatementType(stmt);
11946
13335
  isDmlStatement = writesKintone(stmt);
11947
- needsCompleteInput = requiresCompleteInput(stmt);
11948
13336
  hasWhere = hasWhereClause(stmt);
11949
13337
  insertValuesCount = getInsertValuesCount(stmt);
11950
13338
  const supported = stmtType === "SELECT" || stmtType === "UNION" || stmtType === "WITH" || stmtType === "EXPLAIN" || stmtType === "SHOW_APPS" || stmtType === "DESCRIBE" || stmtType === "ASSERT" || isDmlType(stmtType);
@@ -11991,10 +13379,13 @@ async function run() {
11991
13379
  const yes = args.yes || envBool("KSQL_YES") === true || Boolean(profile.dml?.yes);
11992
13380
  const allowWithoutWhere = args.allowWithoutWhere || envBool("KSQL_ALLOW_WITHOUT_WHERE") === true || Boolean(profile.dml?.allowWithoutWhere);
11993
13381
  const dmlMaxRows = args.dmlMaxRows ?? envInt2("KSQL_DML_MAX_ROWS") ?? profile.dml?.maxRows ?? 100;
11994
- const dmlForcesOnLimitError = needsCompleteInput;
11995
- const effectiveOnLimit = dmlForcesOnLimitError ? "error" : onLimit;
11996
- if (dmlForcesOnLimitError && onLimit === "truncate" && !quiet && !args.dryRun) {
11997
- process.stderr.write(isDmlStatement || batchContainsDml ? "note: onLimit=truncate is ignored for DML (forced to error)\n" : "note: onLimit=truncate is ignored for VALIDATE ONLY (forced to error)\n");
13382
+ const isValidationOnly = batchAnalysis?.containsValidationOnly === true || parsedStmt !== null && typeof parsedStmt === "object" && "validateOnly" in parsedStmt && parsedStmt.validateOnly === true;
13383
+ const surfaceForcesOnLimitError = isDmlStatement || batchContainsDml || isValidationOnly;
13384
+ const effectiveOnLimit = surfaceForcesOnLimitError ? "error" : onLimit;
13385
+ if (surfaceForcesOnLimitError && onLimit === "truncate" && !quiet && !args.dryRun) {
13386
+ const reason = isDmlStatement || batchContainsDml ? "DML" : "VALIDATE ONLY";
13387
+ process.stderr.write(`note: onLimit=truncate is ignored for ${reason} (forced to error)
13388
+ `);
11998
13389
  }
11999
13390
  if (format === "markdown" && noHeader) {
12000
13391
  process.stderr.write("ArgumentError: --no-header cannot be used with --format markdown|md.\n");
@@ -12025,30 +13416,6 @@ async function run() {
12025
13416
  process.stderr.write("ArgumentError: DML is disabled. Use --allow-dml to enable UPDATE/DELETE/INSERT/UPSERT/REORDER.\n");
12026
13417
  return 2;
12027
13418
  }
12028
- if (args.dryRun) {
12029
- let plans;
12030
- try {
12031
- plans = buildBatchExplainPlans(sql, args.variables);
12032
- } catch (err) {
12033
- const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
12034
- bindings: sqlDiagnosticContext.appBindingByMappedApp,
12035
- rewriteSegments: sqlDiagnosticContext.rewriteSegments
12036
- }) : err;
12037
- process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
12038
- `);
12039
- return toExitCodeFromError(restored);
12040
- }
12041
- const out = [];
12042
- const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
12043
- restoredStatements.forEach((p) => {
12044
- if (p.index > 0) out.push("");
12045
- out.push(`[${p.index + 1}] ${p.type}`);
12046
- out.push(...p.plan);
12047
- });
12048
- process.stdout.write(`${out.join("\n")}
12049
- `);
12050
- return 0;
12051
- }
12052
13419
  }
12053
13420
  if (isDmlStatement) {
12054
13421
  if (hasProfileSyntax && stmtType === "DELETE") {
@@ -12075,7 +13442,7 @@ async function run() {
12075
13442
  appProfileByApp.set(appId, appBindingByMappedApp.get(appId)?.profile ?? profileName.toLowerCase());
12076
13443
  }
12077
13444
  const cacheContext = buildCacheContext(profileName, appBindingByMappedApp);
12078
- if (args.dryRun) {
13445
+ if (args.dryRun && !dryRunNeedsMetadata) {
12079
13446
  client = createDryRunClient();
12080
13447
  } else {
12081
13448
  for (const explicitProfile of appProfileByApp.values()) {
@@ -12309,6 +13676,29 @@ async function run() {
12309
13676
  maxDelayMs: args.retryMaxDelay ?? profile.query?.retryMaxDelayMs
12310
13677
  })));
12311
13678
  }
13679
+ if (isBatchSql && args.dryRun) {
13680
+ try {
13681
+ const plans = await buildBatchExplainPlans(sql, client, args.variables, cacheContext, maxRecords);
13682
+ const out = [];
13683
+ const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
13684
+ restoredStatements.forEach((p) => {
13685
+ if (p.index > 0) out.push("");
13686
+ out.push(`[${p.index + 1}] ${p.type}`);
13687
+ out.push(...p.plan);
13688
+ });
13689
+ process.stdout.write(`${out.join("\n")}
13690
+ `);
13691
+ return 0;
13692
+ } catch (err) {
13693
+ const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
13694
+ bindings: sqlDiagnosticContext.appBindingByMappedApp,
13695
+ rewriteSegments: sqlDiagnosticContext.rewriteSegments
13696
+ }) : err;
13697
+ process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
13698
+ `);
13699
+ return toExitCodeFromError(restored);
13700
+ }
13701
+ }
12312
13702
  try {
12313
13703
  if (isDmlStatement && !args.dryRun) {
12314
13704
  const stmtAppId = parsedStmt && typeof parsedStmt === "object" && typeof parsedStmt.appId === "number" ? parsedStmt.appId : appIds[0];