@rex0220/kintone-sql-tools 2.15.0 → 2.17.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
@@ -88,6 +88,9 @@ var KEYWORDS = /* @__PURE__ */ new Map([
88
88
  ["MAX", "MAX" /* MAX */],
89
89
  ["MIN", "MIN" /* MIN */],
90
90
  ["GROUP_CONCAT", "GROUP_CONCAT" /* GROUP_CONCAT */],
91
+ ["ROW_NUMBER", "ROW_NUMBER" /* ROW_NUMBER */],
92
+ ["RANK", "RANK" /* RANK */],
93
+ ["DENSE_RANK", "DENSE_RANK" /* DENSE_RANK */],
91
94
  ["ASSERT", "ASSERT" /* ASSERT */],
92
95
  ["AND", "AND" /* AND */],
93
96
  ["OR", "OR" /* OR */],
@@ -120,6 +123,11 @@ var KEYWORDS = /* @__PURE__ */ new Map([
120
123
  ["COALESCE", "COALESCE" /* COALESCE */],
121
124
  ["NULLIF", "NULLIF" /* NULLIF */],
122
125
  ["ISNULL", "ISNULL" /* ISNULL */],
126
+ ["INSTR", "INSTR" /* INSTR */],
127
+ ["GREATEST", "GREATEST" /* GREATEST */],
128
+ ["LEAST", "LEAST" /* LEAST */],
129
+ ["LPAD", "LPAD" /* LPAD */],
130
+ ["RPAD", "RPAD" /* RPAD */],
123
131
  ["CAST", "CAST" /* CAST */],
124
132
  ["CONVERT", "CONVERT" /* CONVERT */],
125
133
  ["FORMAT", "FORMAT" /* FORMAT */],
@@ -127,6 +135,8 @@ var KEYWORDS = /* @__PURE__ */ new Map([
127
135
  ["FLOOR", "FLOOR" /* FLOOR */],
128
136
  ["CEIL", "CEIL" /* CEIL */],
129
137
  ["CEILING", "CEILING" /* CEILING */],
138
+ ["TRUNCATE", "TRUNCATE" /* TRUNCATE */],
139
+ ["TRUNC", "TRUNC" /* TRUNC */],
130
140
  ["ABS", "ABS" /* ABS */],
131
141
  ["MOD", "MOD" /* MOD */],
132
142
  ["POWER", "POWER" /* POWER */],
@@ -138,6 +148,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
138
148
  ["DATE_FORMAT", "DATE_FORMAT" /* DATE_FORMAT */],
139
149
  ["DATEDIFF", "DATEDIFF" /* DATEDIFF */],
140
150
  ["DATE_ADD", "DATE_ADD" /* DATE_ADD */],
151
+ ["LAST_DAY", "LAST_DAY" /* LAST_DAY */],
141
152
  ["IF", "IF" /* IF */]
142
153
  ]);
143
154
 
@@ -464,6 +475,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
464
475
  "AVG" /* AVG */,
465
476
  "MAX" /* MAX */,
466
477
  "MIN" /* MIN */,
478
+ "ROW_NUMBER" /* ROW_NUMBER */,
479
+ "RANK" /* RANK */,
480
+ "DENSE_RANK" /* DENSE_RANK */,
467
481
  "TODAY" /* TODAY */,
468
482
  "NOW" /* NOW */,
469
483
  "LOGINUSER" /* LOGINUSER */,
@@ -480,6 +494,13 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
480
494
  "COALESCE" /* COALESCE */,
481
495
  "NULLIF" /* NULLIF */,
482
496
  "ISNULL" /* ISNULL */,
497
+ "LEFT" /* LEFT */,
498
+ "RIGHT" /* RIGHT */,
499
+ "INSTR" /* INSTR */,
500
+ "GREATEST" /* GREATEST */,
501
+ "LEAST" /* LEAST */,
502
+ "LPAD" /* LPAD */,
503
+ "RPAD" /* RPAD */,
483
504
  "CAST" /* CAST */,
484
505
  "CONVERT" /* CONVERT */,
485
506
  "FORMAT" /* FORMAT */,
@@ -487,6 +508,8 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
487
508
  "FLOOR" /* FLOOR */,
488
509
  "CEIL" /* CEIL */,
489
510
  "CEILING" /* CEILING */,
511
+ "TRUNCATE" /* TRUNCATE */,
512
+ "TRUNC" /* TRUNC */,
490
513
  "ABS" /* ABS */,
491
514
  "MOD" /* MOD */,
492
515
  "POWER" /* POWER */,
@@ -498,6 +521,7 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
498
521
  "DATE_FORMAT" /* DATE_FORMAT */,
499
522
  "DATEDIFF" /* DATEDIFF */,
500
523
  "DATE_ADD" /* DATE_ADD */,
524
+ "LAST_DAY" /* LAST_DAY */,
501
525
  "IF" /* IF */
502
526
  ]);
503
527
  function needsSpaceBetween(prev, cur) {
@@ -982,6 +1006,11 @@ var Parser = class {
982
1006
  const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
983
1007
  const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
984
1008
  const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
1009
+ const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
1010
+ const hasAggregate = columns.some((column) => this.selectColumnHasAggregate(column));
1011
+ if (hasWindow && (groupBy.length > 0 || hasAggregate)) {
1012
+ throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306F GROUP BY / \u96C6\u8A08\u95A2\u6570\u3068\u540C\u3058 SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
1013
+ }
985
1014
  return {
986
1015
  type: "SELECT",
987
1016
  distinct,
@@ -1046,6 +1075,10 @@ var Parser = class {
1046
1075
  if (this.consume("*" /* STAR */)) {
1047
1076
  return { type: "WILDCARD" };
1048
1077
  }
1078
+ const windowFunc = this.tryWindowFunc();
1079
+ if (windowFunc !== null) {
1080
+ return this.parseWindowColumn(windowFunc);
1081
+ }
1049
1082
  if (this.peek().kind === "CASE" /* CASE */) {
1050
1083
  const expr = this.parseCaseWhenExpr();
1051
1084
  const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
@@ -1117,6 +1150,56 @@ var Parser = class {
1117
1150
  const alias = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
1118
1151
  return { type: "FIELD", field, alias };
1119
1152
  }
1153
+ tryWindowFunc() {
1154
+ switch (this.peek().kind) {
1155
+ case "ROW_NUMBER" /* ROW_NUMBER */:
1156
+ return "ROW_NUMBER";
1157
+ case "RANK" /* RANK */:
1158
+ return "RANK";
1159
+ case "DENSE_RANK" /* DENSE_RANK */:
1160
+ return "DENSE_RANK";
1161
+ default:
1162
+ return null;
1163
+ }
1164
+ }
1165
+ parseWindowColumn(func) {
1166
+ this.advance();
1167
+ this.expect("(" /* LPAREN */);
1168
+ if (this.peek().kind !== ")" /* RPAREN */) {
1169
+ throw new ParseError(`${func} \u306F\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u305B\u3093`, this.peek());
1170
+ }
1171
+ this.expect(")" /* RPAREN */);
1172
+ this.expectSoftKeyword("OVER", `${func} \u306B\u306F OVER (...) \u304C\u5FC5\u8981\u3067\u3059`);
1173
+ this.expect("(" /* LPAREN */);
1174
+ const partitionBy = [];
1175
+ if (this.isSoftKeyword("PARTITION")) {
1176
+ this.advance();
1177
+ this.expect("BY" /* BY */, "PARTITION \u306E\u5F8C\u306B\u306F BY \u304C\u5FC5\u8981\u3067\u3059");
1178
+ do {
1179
+ const ref = this.parseQualifiedIdent();
1180
+ partitionBy.push({ type: "FIELD", tableAlias: ref.tableAlias, field: ref.field });
1181
+ } while (this.consume("," /* COMMA */));
1182
+ }
1183
+ const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
1184
+ this.expect(")" /* RPAREN */);
1185
+ if (!this.consume("AS" /* AS */)) {
1186
+ throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
1187
+ }
1188
+ const alias = this.parseAliasName();
1189
+ return { type: "WINDOW_COL", func, partitionBy, orderBy, alias };
1190
+ }
1191
+ selectColumnHasAggregate(column) {
1192
+ if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
1193
+ if (column.type !== "STRFUNC_COL") return false;
1194
+ return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
1195
+ }
1196
+ stringFuncArgHasAggregate(arg) {
1197
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
1198
+ if (arg.type === "STRING_FUNC") {
1199
+ return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
1200
+ }
1201
+ return false;
1202
+ }
1120
1203
  isArithOp(kind) {
1121
1204
  return kind === "+" /* PLUS */ || kind === "-" /* MINUS */ || kind === "*" /* STAR */ || kind === "/" /* SLASH */ || kind === "%" /* PERCENT */;
1122
1205
  }
@@ -1311,6 +1394,10 @@ var Parser = class {
1311
1394
  // arg: 文字列リテラル / 算術式(フィールド参照・数値含む)/ ネスト文字列関数
1312
1395
  // ──────────────────────────────────────────────────
1313
1396
  tryStringFuncName() {
1397
+ if (this.peekAt(1).kind === "(" /* LPAREN */) {
1398
+ if (this.peek().kind === "LEFT" /* LEFT */) return "LEFT";
1399
+ if (this.peek().kind === "RIGHT" /* RIGHT */) return "RIGHT";
1400
+ }
1314
1401
  const map = {
1315
1402
  ["UPPER" /* UPPER */]: "UPPER",
1316
1403
  ["LOWER" /* LOWER */]: "LOWER",
@@ -1325,6 +1412,11 @@ var Parser = class {
1325
1412
  ["COALESCE" /* COALESCE */]: "COALESCE",
1326
1413
  ["NULLIF" /* NULLIF */]: "NULLIF",
1327
1414
  ["ISNULL" /* ISNULL */]: "ISNULL",
1415
+ ["INSTR" /* INSTR */]: "INSTR",
1416
+ ["GREATEST" /* GREATEST */]: "GREATEST",
1417
+ ["LEAST" /* LEAST */]: "LEAST",
1418
+ ["LPAD" /* LPAD */]: "LPAD",
1419
+ ["RPAD" /* RPAD */]: "RPAD",
1328
1420
  ["CAST" /* CAST */]: "CAST",
1329
1421
  ["CONVERT" /* CONVERT */]: "CAST",
1330
1422
  // CONVERT → CAST に正規化
@@ -1334,12 +1426,16 @@ var Parser = class {
1334
1426
  ["CEIL" /* CEIL */]: "CEIL",
1335
1427
  ["CEILING" /* CEILING */]: "CEIL",
1336
1428
  // CEILING → CEIL に正規化
1429
+ ["TRUNCATE" /* TRUNCATE */]: "TRUNCATE",
1430
+ ["TRUNC" /* TRUNC */]: "TRUNCATE",
1431
+ // TRUNC → TRUNCATE に正規化
1337
1432
  ["YEAR" /* YEAR */]: "YEAR",
1338
1433
  ["MONTH" /* MONTH */]: "MONTH",
1339
1434
  ["DAY" /* DAY */]: "DAY",
1340
1435
  ["DATE_FORMAT" /* DATE_FORMAT */]: "DATE_FORMAT",
1341
1436
  ["DATEDIFF" /* DATEDIFF */]: "DATEDIFF",
1342
1437
  ["DATE_ADD" /* DATE_ADD */]: "DATE_ADD",
1438
+ ["LAST_DAY" /* LAST_DAY */]: "LAST_DAY",
1343
1439
  ["ABS" /* ABS */]: "ABS",
1344
1440
  ["MOD" /* MOD */]: "MOD",
1345
1441
  ["POWER" /* POWER */]: "POWER",
@@ -2898,12 +2994,16 @@ var KintoneQueryError = class extends Error {
2898
2994
  };
2899
2995
 
2900
2996
  // src/converter/selectToKintone.ts
2997
+ function hasWindowColumns(columns) {
2998
+ return columns.some((column) => column.type === "WINDOW_COL");
2999
+ }
2901
3000
  function resolveSelectMode(stmt) {
2902
3001
  if (stmt.from.subtableCode) return "FULL_SCAN";
2903
3002
  if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
2904
3003
  if (stmt.joins.length > 0) return "FULL_SCAN";
2905
3004
  if (stmt.groupBy.length > 0) return "FULL_SCAN";
2906
3005
  if (stmt.distinct) return "FULL_SCAN";
3006
+ if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
2907
3007
  if (stmt.columns.some(
2908
3008
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
2909
3009
  )) return "FULL_SCAN";
@@ -3275,16 +3375,16 @@ function collectRequiredFieldsByTable(stmt) {
3275
3375
  }
3276
3376
  walkStringFunc(k.expr, "groupBy");
3277
3377
  };
3278
- const walkOrderByKey = (k) => {
3378
+ const walkOrderByKey = (k, phase = "orderBy") => {
3279
3379
  if (k.type === "FIELD_NAME") {
3280
- addFieldName(k.name, "orderBy");
3380
+ addFieldName(k.name, phase);
3281
3381
  return;
3282
3382
  }
3283
3383
  if (k.type === "ARITH_KEY") {
3284
- walkArith(k.expr, "orderBy");
3384
+ walkArith(k.expr, phase);
3285
3385
  return;
3286
3386
  }
3287
- walkStringFunc(k.expr, "orderBy");
3387
+ walkStringFunc(k.expr, phase);
3288
3388
  };
3289
3389
  for (const col of stmt.columns) {
3290
3390
  switch (col.type) {
@@ -3316,6 +3416,10 @@ function collectRequiredFieldsByTable(stmt) {
3316
3416
  break;
3317
3417
  case "SCALAR_SUBQUERY_COL":
3318
3418
  break;
3419
+ case "WINDOW_COL":
3420
+ for (const ref of col.partitionBy) addFieldRef(ref.field, ref.tableAlias, "select");
3421
+ for (const item of col.orderBy) walkOrderByKey(item.key, "select");
3422
+ break;
3319
3423
  }
3320
3424
  }
3321
3425
  for (const join2 of stmt.joins) {
@@ -3362,6 +3466,10 @@ function collectSelectOutputNames(columns) {
3362
3466
  }
3363
3467
  if (col.type === "SCALAR_SUBQUERY_COL") {
3364
3468
  names.add(col.alias ?? "(subquery)");
3469
+ continue;
3470
+ }
3471
+ if (col.type === "WINDOW_COL") {
3472
+ names.add(col.alias);
3365
3473
  }
3366
3474
  }
3367
3475
  return names;
@@ -4066,6 +4174,25 @@ function compareScalarValues(op, leftStr, rightStr) {
4066
4174
  return numeric ? leftNum <= rightNum : leftStr <= rightStr;
4067
4175
  }
4068
4176
  }
4177
+ function selectScalarExtreme(values, extreme) {
4178
+ if (extreme === "least" && values.includes("")) return "";
4179
+ const candidates = extreme === "greatest" ? values.filter((value) => value !== "") : [...values];
4180
+ if (candidates.length === 0) return "";
4181
+ const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
4182
+ const compare = (left, right) => {
4183
+ if (numeric) {
4184
+ const leftNum = Number(left);
4185
+ const rightNum = Number(right);
4186
+ if (leftNum < rightNum) return -1;
4187
+ if (leftNum > rightNum) return 1;
4188
+ }
4189
+ return left < right ? -1 : left > right ? 1 : 0;
4190
+ };
4191
+ return candidates.reduce((best, candidate) => {
4192
+ const cmp = compare(candidate, best);
4193
+ return extreme === "greatest" ? cmp > 0 ? candidate : best : cmp < 0 ? candidate : best;
4194
+ });
4195
+ }
4069
4196
 
4070
4197
  // src/engine/evalFunc.ts
4071
4198
  function evalArithExpr(expr, row) {
@@ -4114,6 +4241,36 @@ function evalStringFunc(expr, row) {
4114
4241
  const len = args[2] !== void 0 ? Number(args[2]) : void 0;
4115
4242
  return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
4116
4243
  }
4244
+ case "LEFT": {
4245
+ assertArity("LEFT", args, 2, 2);
4246
+ const str = args[0];
4247
+ const n = Math.trunc(Number(args[1]));
4248
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
4249
+ }
4250
+ case "RIGHT": {
4251
+ assertArity("RIGHT", args, 2, 2);
4252
+ const str = args[0];
4253
+ const n = Math.trunc(Number(args[1]));
4254
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
4255
+ }
4256
+ case "INSTR":
4257
+ assertArity("INSTR", args, 2, 2);
4258
+ return String(args[0].indexOf(args[1]) + 1);
4259
+ case "LPAD":
4260
+ case "RPAD": {
4261
+ assertArity(expr.func, args, 2, 3);
4262
+ const str = args[0];
4263
+ const n = Math.trunc(Number(args[1]));
4264
+ if (Number.isNaN(n) || n <= 0) return "";
4265
+ if (str.length >= n) return str.slice(0, n);
4266
+ const pad = args[2] ?? " ";
4267
+ if (pad === "") return str;
4268
+ return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
4269
+ }
4270
+ case "GREATEST":
4271
+ case "LEAST":
4272
+ assertArity(expr.func, args, 2);
4273
+ return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
4117
4274
  case "CONCAT":
4118
4275
  return args.join("");
4119
4276
  case "REPLACE": {
@@ -4134,6 +4291,9 @@ function evalStringFunc(expr, row) {
4134
4291
  return applyRoundOp("floor", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
4135
4292
  case "CEIL":
4136
4293
  return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
4294
+ case "TRUNCATE":
4295
+ assertArity("TRUNCATE", args, 1, 2);
4296
+ return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
4137
4297
  case "CAST": {
4138
4298
  const val = args[0] ?? "";
4139
4299
  const castType = args[1] ?? "TEXT";
@@ -4166,6 +4326,9 @@ function evalStringFunc(expr, row) {
4166
4326
  return applyDateDiff(args[0] ?? "", args[1] ?? "");
4167
4327
  case "DATE_ADD":
4168
4328
  return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
4329
+ case "LAST_DAY":
4330
+ assertArity("LAST_DAY", args, 1, 1);
4331
+ return applyLastDay(args[0]);
4169
4332
  case "ABS":
4170
4333
  return String(Math.abs(Number(args[0] ?? "0")));
4171
4334
  case "MOD": {
@@ -4188,6 +4351,11 @@ function evalStringFunc(expr, row) {
4188
4351
  return (/* @__PURE__ */ new Date()).toISOString();
4189
4352
  }
4190
4353
  }
4354
+ function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
4355
+ if (args.length >= min && args.length <= max) return;
4356
+ const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
4357
+ throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
4358
+ }
4191
4359
  function parseDateParts(s) {
4192
4360
  return {
4193
4361
  y: s.slice(0, 4) || "0000",
@@ -4212,6 +4380,9 @@ function applyDateDiff(date1, date2) {
4212
4380
  return String(Math.round((d1 - d2) / 864e5));
4213
4381
  }
4214
4382
  function applyDateAdd(dateStr, n, unit) {
4383
+ if (unit !== "YEAR" && unit !== "MONTH" && unit !== "DAY") {
4384
+ throw new Error("ArgumentError: DATE_ADD unit must be YEAR, MONTH, or DAY.");
4385
+ }
4215
4386
  if (!dateStr || dateStr.length < 10) return dateStr;
4216
4387
  const { y, mo, d } = parseDateParts(dateStr);
4217
4388
  const dt = new Date(Date.UTC(+y, +mo - 1, +d));
@@ -4222,7 +4393,7 @@ function applyDateAdd(dateStr, n, unit) {
4222
4393
  case "MONTH":
4223
4394
  dt.setUTCMonth(dt.getUTCMonth() + n);
4224
4395
  break;
4225
- default:
4396
+ case "DAY":
4226
4397
  dt.setUTCDate(dt.getUTCDate() + n);
4227
4398
  break;
4228
4399
  }
@@ -4231,6 +4402,15 @@ function applyDateAdd(dateStr, n, unit) {
4231
4402
  const rd = String(dt.getUTCDate()).padStart(2, "0");
4232
4403
  return `${ry}-${rmo}-${rd}`;
4233
4404
  }
4405
+ function applyLastDay(dateStr) {
4406
+ if (!dateStr || dateStr.length < 10) return dateStr;
4407
+ const { y, mo } = parseDateParts(dateStr);
4408
+ const dt = new Date(Date.UTC(+y, +mo, 0));
4409
+ const ry = String(dt.getUTCFullYear()).padStart(4, "0");
4410
+ const rmo = String(dt.getUTCMonth() + 1).padStart(2, "0");
4411
+ const rd = String(dt.getUTCDate()).padStart(2, "0");
4412
+ return `${ry}-${rmo}-${rd}`;
4413
+ }
4234
4414
  function applyFormat(num, pattern) {
4235
4415
  if (/^-?\d+$/.test(pattern.trim())) {
4236
4416
  return formatWithComma(num, Math.max(0, Number(pattern)));
@@ -5309,6 +5489,10 @@ function buildDistinctKeyBuilder(rows, columns) {
5309
5489
  values.push(row[col.field] ?? "");
5310
5490
  continue;
5311
5491
  }
5492
+ if (col.type === "WINDOW_COL") {
5493
+ values.push(row[col.alias] ?? "");
5494
+ continue;
5495
+ }
5312
5496
  if (col.type === "PARENT_WILDCARD") {
5313
5497
  for (const k of sortedParentKeys) {
5314
5498
  values.push(row[k] !== void 0 ? row[k] : null);
@@ -5320,6 +5504,9 @@ function buildDistinctKeyBuilder(rows, columns) {
5320
5504
  }
5321
5505
  function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
5322
5506
  if (orderBy.length === 0) return rows;
5507
+ return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
5508
+ }
5509
+ function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
5323
5510
  const keyMeta = orderBy.map(({ key }) => ({
5324
5511
  orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
5325
5512
  sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
@@ -5338,14 +5525,16 @@ function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
5338
5525
  };
5339
5526
  })
5340
5527
  }));
5341
- decorated.sort((a, b) => {
5342
- for (let i = 0; i < orderBy.length; i++) {
5343
- const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
5344
- if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
5345
- }
5346
- return 0;
5347
- });
5348
- return decorated.map((d) => d.row);
5528
+ const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
5529
+ decorated.sort(compare);
5530
+ return { rows: decorated, compare };
5531
+ }
5532
+ function compareDecoratedRows(a, b, orderBy, keyMeta) {
5533
+ for (let i = 0; i < orderBy.length; i++) {
5534
+ const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
5535
+ if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
5536
+ }
5537
+ return 0;
5349
5538
  }
5350
5539
  function compareSortKeys(a, b, meta) {
5351
5540
  if (meta.orderMap) {
@@ -5390,6 +5579,38 @@ function minChoiceIndex(values, orderMap) {
5390
5579
  }
5391
5580
  return min;
5392
5581
  }
5582
+ function applyWindow(rows, columns, optionOrders, sortKinds) {
5583
+ const windows = columns.filter((column) => column.type === "WINDOW_COL");
5584
+ if (rows.length === 0 || windows.length === 0) return rows;
5585
+ for (const window of windows) {
5586
+ const partitions = /* @__PURE__ */ new Map();
5587
+ for (const row of rows) {
5588
+ const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
5589
+ const partition = partitions.get(key);
5590
+ if (partition) partition.push(row);
5591
+ else partitions.set(key, [row]);
5592
+ }
5593
+ for (const partition of partitions.values()) {
5594
+ const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
5595
+ const sorted = sortedResult.rows;
5596
+ let rank = 1;
5597
+ let denseRank = 1;
5598
+ for (let index = 0; index < sorted.length; index++) {
5599
+ if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
5600
+ rank = index + 1;
5601
+ denseRank++;
5602
+ }
5603
+ const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
5604
+ sorted[index].row[window.alias] = String(value);
5605
+ }
5606
+ }
5607
+ }
5608
+ return rows;
5609
+ }
5610
+ function resolveWindowField(row, ref) {
5611
+ const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
5612
+ return resolveFieldRef(row, name);
5613
+ }
5393
5614
  function applyLimit(rows, limit, offset) {
5394
5615
  const start = offset ?? 0;
5395
5616
  if (limit === null) return rows.slice(start);
@@ -5480,6 +5701,12 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
5480
5701
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
5481
5702
  break;
5482
5703
  }
5704
+ case "WINDOW_COL": {
5705
+ const key = outputKeys?.[colIdx] ?? col.alias;
5706
+ out[key] = row[col.alias] ?? "";
5707
+ if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
5708
+ break;
5709
+ }
5483
5710
  }
5484
5711
  }
5485
5712
  return out;
@@ -5520,6 +5747,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
5520
5747
  return col.alias ?? stringFuncDefaultKey(col.expr);
5521
5748
  case "SCALAR_SUBQUERY_COL":
5522
5749
  return col.alias ?? "(subquery)";
5750
+ case "WINDOW_COL":
5751
+ return col.alias;
5523
5752
  case "WILDCARD":
5524
5753
  case "PARENT_WILDCARD":
5525
5754
  throw new Error("internal: computeOutputKey received a wildcard column");
@@ -5634,6 +5863,7 @@ function runFullScan(input) {
5634
5863
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
5635
5864
  }
5636
5865
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
5866
+ rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
5637
5867
  if (stmt.distinct) {
5638
5868
  rows = applyDistinct(rows, stmt.columns);
5639
5869
  }
@@ -6578,6 +6808,11 @@ function validateNoFromColumns(stmt) {
6578
6808
  throw new Error("ArgumentError: field reference is not allowed without FROM.");
6579
6809
  }
6580
6810
  break;
6811
+ case "WINDOW_COL":
6812
+ if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
6813
+ throw new Error("ArgumentError: field reference is not allowed without FROM.");
6814
+ }
6815
+ break;
6581
6816
  default:
6582
6817
  throw new Error(`ArgumentError: ${col.type} is not supported without FROM.`);
6583
6818
  }
@@ -6588,7 +6823,8 @@ function executeNoFromSelect(stmt) {
6588
6823
  throw new Error("ArgumentError: JOIN/WHERE/GROUP BY/HAVING/ORDER BY/DISTINCT are not supported without FROM.");
6589
6824
  }
6590
6825
  validateNoFromColumns(stmt);
6591
- const { rows: projected, columns } = project([{}], stmt.columns);
6826
+ const windowed = applyWindow([{}], stmt.columns);
6827
+ const { rows: projected, columns } = project(windowed, stmt.columns);
6592
6828
  const rows = applyLimit(projected, stmt.limit, stmt.offset);
6593
6829
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
6594
6830
  }
@@ -7012,6 +7248,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
7012
7248
  meta = { sortKind: "number" };
7013
7249
  } else if (column.type === "LITERAL_COL") {
7014
7250
  meta = { sortKind: "string" };
7251
+ } else if (column.type === "WINDOW_COL") {
7252
+ meta = { sortKind: "number" };
7015
7253
  }
7016
7254
  if (meta) inferred.set(output, meta);
7017
7255
  });
@@ -7597,7 +7835,10 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
7597
7835
  return map;
7598
7836
  }
7599
7837
  async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
7600
- if (stmt.orderBy.length === 0) {
7838
+ const hasWindowOrderBy = stmt.columns.some(
7839
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
7840
+ );
7841
+ if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
7601
7842
  return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
7602
7843
  }
7603
7844
  const [optionOrders, sortKinds] = await Promise.all([
@@ -9059,6 +9300,8 @@ function collectFullScanReasons(stmt) {
9059
9300
  r.push("DISTINCT \u3042\u308A");
9060
9301
  if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))
9061
9302
  r.push("\u96C6\u8A08\u95A2\u6570\uFF08COUNT / SUM \u7B49\uFF09\u3042\u308A");
9303
+ if (stmt.columns.some((c) => c.type === "WINDOW_COL"))
9304
+ r.push("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u3042\u308A");
9062
9305
  if (stmt.columns.some((c) => c.type === "SCALAR_SUBQUERY_COL"))
9063
9306
  r.push("SELECT \u5217\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA");
9064
9307
  if (whereRequiresJsEval(stmt.where))
@@ -31000,6 +31000,9 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31000
31000
  ["MAX", "MAX" /* MAX */],
31001
31001
  ["MIN", "MIN" /* MIN */],
31002
31002
  ["GROUP_CONCAT", "GROUP_CONCAT" /* GROUP_CONCAT */],
31003
+ ["ROW_NUMBER", "ROW_NUMBER" /* ROW_NUMBER */],
31004
+ ["RANK", "RANK" /* RANK */],
31005
+ ["DENSE_RANK", "DENSE_RANK" /* DENSE_RANK */],
31003
31006
  ["ASSERT", "ASSERT" /* ASSERT */],
31004
31007
  ["AND", "AND" /* AND */],
31005
31008
  ["OR", "OR" /* OR */],
@@ -31032,6 +31035,11 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31032
31035
  ["COALESCE", "COALESCE" /* COALESCE */],
31033
31036
  ["NULLIF", "NULLIF" /* NULLIF */],
31034
31037
  ["ISNULL", "ISNULL" /* ISNULL */],
31038
+ ["INSTR", "INSTR" /* INSTR */],
31039
+ ["GREATEST", "GREATEST" /* GREATEST */],
31040
+ ["LEAST", "LEAST" /* LEAST */],
31041
+ ["LPAD", "LPAD" /* LPAD */],
31042
+ ["RPAD", "RPAD" /* RPAD */],
31035
31043
  ["CAST", "CAST" /* CAST */],
31036
31044
  ["CONVERT", "CONVERT" /* CONVERT */],
31037
31045
  ["FORMAT", "FORMAT" /* FORMAT */],
@@ -31039,6 +31047,8 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31039
31047
  ["FLOOR", "FLOOR" /* FLOOR */],
31040
31048
  ["CEIL", "CEIL" /* CEIL */],
31041
31049
  ["CEILING", "CEILING" /* CEILING */],
31050
+ ["TRUNCATE", "TRUNCATE" /* TRUNCATE */],
31051
+ ["TRUNC", "TRUNC" /* TRUNC */],
31042
31052
  ["ABS", "ABS" /* ABS */],
31043
31053
  ["MOD", "MOD" /* MOD */],
31044
31054
  ["POWER", "POWER" /* POWER */],
@@ -31050,6 +31060,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
31050
31060
  ["DATE_FORMAT", "DATE_FORMAT" /* DATE_FORMAT */],
31051
31061
  ["DATEDIFF", "DATEDIFF" /* DATEDIFF */],
31052
31062
  ["DATE_ADD", "DATE_ADD" /* DATE_ADD */],
31063
+ ["LAST_DAY", "LAST_DAY" /* LAST_DAY */],
31053
31064
  ["IF", "IF" /* IF */]
31054
31065
  ]);
31055
31066
 
@@ -31376,6 +31387,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31376
31387
  "AVG" /* AVG */,
31377
31388
  "MAX" /* MAX */,
31378
31389
  "MIN" /* MIN */,
31390
+ "ROW_NUMBER" /* ROW_NUMBER */,
31391
+ "RANK" /* RANK */,
31392
+ "DENSE_RANK" /* DENSE_RANK */,
31379
31393
  "TODAY" /* TODAY */,
31380
31394
  "NOW" /* NOW */,
31381
31395
  "LOGINUSER" /* LOGINUSER */,
@@ -31392,6 +31406,13 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31392
31406
  "COALESCE" /* COALESCE */,
31393
31407
  "NULLIF" /* NULLIF */,
31394
31408
  "ISNULL" /* ISNULL */,
31409
+ "LEFT" /* LEFT */,
31410
+ "RIGHT" /* RIGHT */,
31411
+ "INSTR" /* INSTR */,
31412
+ "GREATEST" /* GREATEST */,
31413
+ "LEAST" /* LEAST */,
31414
+ "LPAD" /* LPAD */,
31415
+ "RPAD" /* RPAD */,
31395
31416
  "CAST" /* CAST */,
31396
31417
  "CONVERT" /* CONVERT */,
31397
31418
  "FORMAT" /* FORMAT */,
@@ -31399,6 +31420,8 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31399
31420
  "FLOOR" /* FLOOR */,
31400
31421
  "CEIL" /* CEIL */,
31401
31422
  "CEILING" /* CEILING */,
31423
+ "TRUNCATE" /* TRUNCATE */,
31424
+ "TRUNC" /* TRUNC */,
31402
31425
  "ABS" /* ABS */,
31403
31426
  "MOD" /* MOD */,
31404
31427
  "POWER" /* POWER */,
@@ -31410,6 +31433,7 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31410
31433
  "DATE_FORMAT" /* DATE_FORMAT */,
31411
31434
  "DATEDIFF" /* DATEDIFF */,
31412
31435
  "DATE_ADD" /* DATE_ADD */,
31436
+ "LAST_DAY" /* LAST_DAY */,
31413
31437
  "IF" /* IF */
31414
31438
  ]);
31415
31439
  function needsSpaceBetween(prev, cur) {
@@ -31894,6 +31918,11 @@ var Parser = class {
31894
31918
  const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
31895
31919
  const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
31896
31920
  const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
31921
+ const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
31922
+ const hasAggregate = columns.some((column) => this.selectColumnHasAggregate(column));
31923
+ if (hasWindow && (groupBy.length > 0 || hasAggregate)) {
31924
+ throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306F GROUP BY / \u96C6\u8A08\u95A2\u6570\u3068\u540C\u3058 SELECT \u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
31925
+ }
31897
31926
  return {
31898
31927
  type: "SELECT",
31899
31928
  distinct,
@@ -31958,6 +31987,10 @@ var Parser = class {
31958
31987
  if (this.consume("*" /* STAR */)) {
31959
31988
  return { type: "WILDCARD" };
31960
31989
  }
31990
+ const windowFunc = this.tryWindowFunc();
31991
+ if (windowFunc !== null) {
31992
+ return this.parseWindowColumn(windowFunc);
31993
+ }
31961
31994
  if (this.peek().kind === "CASE" /* CASE */) {
31962
31995
  const expr = this.parseCaseWhenExpr();
31963
31996
  const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
@@ -32029,6 +32062,56 @@ var Parser = class {
32029
32062
  const alias = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
32030
32063
  return { type: "FIELD", field, alias };
32031
32064
  }
32065
+ tryWindowFunc() {
32066
+ switch (this.peek().kind) {
32067
+ case "ROW_NUMBER" /* ROW_NUMBER */:
32068
+ return "ROW_NUMBER";
32069
+ case "RANK" /* RANK */:
32070
+ return "RANK";
32071
+ case "DENSE_RANK" /* DENSE_RANK */:
32072
+ return "DENSE_RANK";
32073
+ default:
32074
+ return null;
32075
+ }
32076
+ }
32077
+ parseWindowColumn(func) {
32078
+ this.advance();
32079
+ this.expect("(" /* LPAREN */);
32080
+ if (this.peek().kind !== ")" /* RPAREN */) {
32081
+ throw new ParseError(`${func} \u306F\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u305B\u3093`, this.peek());
32082
+ }
32083
+ this.expect(")" /* RPAREN */);
32084
+ this.expectSoftKeyword("OVER", `${func} \u306B\u306F OVER (...) \u304C\u5FC5\u8981\u3067\u3059`);
32085
+ this.expect("(" /* LPAREN */);
32086
+ const partitionBy = [];
32087
+ if (this.isSoftKeyword("PARTITION")) {
32088
+ this.advance();
32089
+ this.expect("BY" /* BY */, "PARTITION \u306E\u5F8C\u306B\u306F BY \u304C\u5FC5\u8981\u3067\u3059");
32090
+ do {
32091
+ const ref = this.parseQualifiedIdent();
32092
+ partitionBy.push({ type: "FIELD", tableAlias: ref.tableAlias, field: ref.field });
32093
+ } while (this.consume("," /* COMMA */));
32094
+ }
32095
+ const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
32096
+ this.expect(")" /* RPAREN */);
32097
+ if (!this.consume("AS" /* AS */)) {
32098
+ throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
32099
+ }
32100
+ const alias = this.parseAliasName();
32101
+ return { type: "WINDOW_COL", func, partitionBy, orderBy, alias };
32102
+ }
32103
+ selectColumnHasAggregate(column) {
32104
+ if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
32105
+ if (column.type !== "STRFUNC_COL") return false;
32106
+ return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
32107
+ }
32108
+ stringFuncArgHasAggregate(arg) {
32109
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
32110
+ if (arg.type === "STRING_FUNC") {
32111
+ return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
32112
+ }
32113
+ return false;
32114
+ }
32032
32115
  isArithOp(kind) {
32033
32116
  return kind === "+" /* PLUS */ || kind === "-" /* MINUS */ || kind === "*" /* STAR */ || kind === "/" /* SLASH */ || kind === "%" /* PERCENT */;
32034
32117
  }
@@ -32223,6 +32306,10 @@ var Parser = class {
32223
32306
  // arg: 文字列リテラル / 算術式(フィールド参照・数値含む)/ ネスト文字列関数
32224
32307
  // ──────────────────────────────────────────────────
32225
32308
  tryStringFuncName() {
32309
+ if (this.peekAt(1).kind === "(" /* LPAREN */) {
32310
+ if (this.peek().kind === "LEFT" /* LEFT */) return "LEFT";
32311
+ if (this.peek().kind === "RIGHT" /* RIGHT */) return "RIGHT";
32312
+ }
32226
32313
  const map2 = {
32227
32314
  ["UPPER" /* UPPER */]: "UPPER",
32228
32315
  ["LOWER" /* LOWER */]: "LOWER",
@@ -32237,6 +32324,11 @@ var Parser = class {
32237
32324
  ["COALESCE" /* COALESCE */]: "COALESCE",
32238
32325
  ["NULLIF" /* NULLIF */]: "NULLIF",
32239
32326
  ["ISNULL" /* ISNULL */]: "ISNULL",
32327
+ ["INSTR" /* INSTR */]: "INSTR",
32328
+ ["GREATEST" /* GREATEST */]: "GREATEST",
32329
+ ["LEAST" /* LEAST */]: "LEAST",
32330
+ ["LPAD" /* LPAD */]: "LPAD",
32331
+ ["RPAD" /* RPAD */]: "RPAD",
32240
32332
  ["CAST" /* CAST */]: "CAST",
32241
32333
  ["CONVERT" /* CONVERT */]: "CAST",
32242
32334
  // CONVERT → CAST に正規化
@@ -32246,12 +32338,16 @@ var Parser = class {
32246
32338
  ["CEIL" /* CEIL */]: "CEIL",
32247
32339
  ["CEILING" /* CEILING */]: "CEIL",
32248
32340
  // CEILING → CEIL に正規化
32341
+ ["TRUNCATE" /* TRUNCATE */]: "TRUNCATE",
32342
+ ["TRUNC" /* TRUNC */]: "TRUNCATE",
32343
+ // TRUNC → TRUNCATE に正規化
32249
32344
  ["YEAR" /* YEAR */]: "YEAR",
32250
32345
  ["MONTH" /* MONTH */]: "MONTH",
32251
32346
  ["DAY" /* DAY */]: "DAY",
32252
32347
  ["DATE_FORMAT" /* DATE_FORMAT */]: "DATE_FORMAT",
32253
32348
  ["DATEDIFF" /* DATEDIFF */]: "DATEDIFF",
32254
32349
  ["DATE_ADD" /* DATE_ADD */]: "DATE_ADD",
32350
+ ["LAST_DAY" /* LAST_DAY */]: "LAST_DAY",
32255
32351
  ["ABS" /* ABS */]: "ABS",
32256
32352
  ["MOD" /* MOD */]: "MOD",
32257
32353
  ["POWER" /* POWER */]: "POWER",
@@ -33798,12 +33894,16 @@ var KintoneQueryError = class extends Error {
33798
33894
  };
33799
33895
 
33800
33896
  // src/converter/selectToKintone.ts
33897
+ function hasWindowColumns(columns) {
33898
+ return columns.some((column) => column.type === "WINDOW_COL");
33899
+ }
33801
33900
  function resolveSelectMode(stmt) {
33802
33901
  if (stmt.from.subtableCode) return "FULL_SCAN";
33803
33902
  if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
33804
33903
  if (stmt.joins.length > 0) return "FULL_SCAN";
33805
33904
  if (stmt.groupBy.length > 0) return "FULL_SCAN";
33806
33905
  if (stmt.distinct) return "FULL_SCAN";
33906
+ if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
33807
33907
  if (stmt.columns.some(
33808
33908
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
33809
33909
  )) return "FULL_SCAN";
@@ -34175,16 +34275,16 @@ function collectRequiredFieldsByTable(stmt) {
34175
34275
  }
34176
34276
  walkStringFunc(k.expr, "groupBy");
34177
34277
  };
34178
- const walkOrderByKey = (k) => {
34278
+ const walkOrderByKey = (k, phase = "orderBy") => {
34179
34279
  if (k.type === "FIELD_NAME") {
34180
- addFieldName(k.name, "orderBy");
34280
+ addFieldName(k.name, phase);
34181
34281
  return;
34182
34282
  }
34183
34283
  if (k.type === "ARITH_KEY") {
34184
- walkArith(k.expr, "orderBy");
34284
+ walkArith(k.expr, phase);
34185
34285
  return;
34186
34286
  }
34187
- walkStringFunc(k.expr, "orderBy");
34287
+ walkStringFunc(k.expr, phase);
34188
34288
  };
34189
34289
  for (const col of stmt.columns) {
34190
34290
  switch (col.type) {
@@ -34216,6 +34316,10 @@ function collectRequiredFieldsByTable(stmt) {
34216
34316
  break;
34217
34317
  case "SCALAR_SUBQUERY_COL":
34218
34318
  break;
34319
+ case "WINDOW_COL":
34320
+ for (const ref of col.partitionBy) addFieldRef(ref.field, ref.tableAlias, "select");
34321
+ for (const item of col.orderBy) walkOrderByKey(item.key, "select");
34322
+ break;
34219
34323
  }
34220
34324
  }
34221
34325
  for (const join of stmt.joins) {
@@ -34262,6 +34366,10 @@ function collectSelectOutputNames(columns) {
34262
34366
  }
34263
34367
  if (col.type === "SCALAR_SUBQUERY_COL") {
34264
34368
  names.add(col.alias ?? "(subquery)");
34369
+ continue;
34370
+ }
34371
+ if (col.type === "WINDOW_COL") {
34372
+ names.add(col.alias);
34265
34373
  }
34266
34374
  }
34267
34375
  return names;
@@ -34966,6 +35074,25 @@ function compareScalarValues(op, leftStr, rightStr) {
34966
35074
  return numeric ? leftNum <= rightNum : leftStr <= rightStr;
34967
35075
  }
34968
35076
  }
35077
+ function selectScalarExtreme(values, extreme) {
35078
+ if (extreme === "least" && values.includes("")) return "";
35079
+ const candidates = extreme === "greatest" ? values.filter((value) => value !== "") : [...values];
35080
+ if (candidates.length === 0) return "";
35081
+ const numeric = candidates.every((value) => !Number.isNaN(Number(value)));
35082
+ const compare = (left, right) => {
35083
+ if (numeric) {
35084
+ const leftNum = Number(left);
35085
+ const rightNum = Number(right);
35086
+ if (leftNum < rightNum) return -1;
35087
+ if (leftNum > rightNum) return 1;
35088
+ }
35089
+ return left < right ? -1 : left > right ? 1 : 0;
35090
+ };
35091
+ return candidates.reduce((best, candidate) => {
35092
+ const cmp = compare(candidate, best);
35093
+ return extreme === "greatest" ? cmp > 0 ? candidate : best : cmp < 0 ? candidate : best;
35094
+ });
35095
+ }
34969
35096
 
34970
35097
  // src/engine/evalFunc.ts
34971
35098
  function evalArithExpr(expr, row) {
@@ -35014,6 +35141,36 @@ function evalStringFunc(expr, row) {
35014
35141
  const len = args[2] !== void 0 ? Number(args[2]) : void 0;
35015
35142
  return len !== void 0 ? str.slice(start, start + len) : str.slice(start);
35016
35143
  }
35144
+ case "LEFT": {
35145
+ assertArity("LEFT", args, 2, 2);
35146
+ const str = args[0];
35147
+ const n = Math.trunc(Number(args[1]));
35148
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(0, n);
35149
+ }
35150
+ case "RIGHT": {
35151
+ assertArity("RIGHT", args, 2, 2);
35152
+ const str = args[0];
35153
+ const n = Math.trunc(Number(args[1]));
35154
+ return Number.isNaN(n) || n <= 0 ? "" : str.slice(Math.max(0, str.length - n));
35155
+ }
35156
+ case "INSTR":
35157
+ assertArity("INSTR", args, 2, 2);
35158
+ return String(args[0].indexOf(args[1]) + 1);
35159
+ case "LPAD":
35160
+ case "RPAD": {
35161
+ assertArity(expr.func, args, 2, 3);
35162
+ const str = args[0];
35163
+ const n = Math.trunc(Number(args[1]));
35164
+ if (Number.isNaN(n) || n <= 0) return "";
35165
+ if (str.length >= n) return str.slice(0, n);
35166
+ const pad = args[2] ?? " ";
35167
+ if (pad === "") return str;
35168
+ return expr.func === "LPAD" ? str.padStart(n, pad) : str.padEnd(n, pad);
35169
+ }
35170
+ case "GREATEST":
35171
+ case "LEAST":
35172
+ assertArity(expr.func, args, 2);
35173
+ return selectScalarExtreme(args, expr.func === "GREATEST" ? "greatest" : "least");
35017
35174
  case "CONCAT":
35018
35175
  return args.join("");
35019
35176
  case "REPLACE": {
@@ -35034,6 +35191,9 @@ function evalStringFunc(expr, row) {
35034
35191
  return applyRoundOp("floor", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
35035
35192
  case "CEIL":
35036
35193
  return applyRoundOp("ceil", Number(args[0] ?? "0"), Number(args[1] ?? "0"));
35194
+ case "TRUNCATE":
35195
+ assertArity("TRUNCATE", args, 1, 2);
35196
+ return applyRoundOp("trunc", Number(args[0]), Number(args[1] ?? "0"));
35037
35197
  case "CAST": {
35038
35198
  const val = args[0] ?? "";
35039
35199
  const castType = args[1] ?? "TEXT";
@@ -35066,6 +35226,9 @@ function evalStringFunc(expr, row) {
35066
35226
  return applyDateDiff(args[0] ?? "", args[1] ?? "");
35067
35227
  case "DATE_ADD":
35068
35228
  return applyDateAdd(args[0] ?? "", Number(args[1] ?? "0"), (args[2] ?? "DAY").toUpperCase());
35229
+ case "LAST_DAY":
35230
+ assertArity("LAST_DAY", args, 1, 1);
35231
+ return applyLastDay(args[0]);
35069
35232
  case "ABS":
35070
35233
  return String(Math.abs(Number(args[0] ?? "0")));
35071
35234
  case "MOD": {
@@ -35088,6 +35251,11 @@ function evalStringFunc(expr, row) {
35088
35251
  return (/* @__PURE__ */ new Date()).toISOString();
35089
35252
  }
35090
35253
  }
35254
+ function assertArity(func, args, min, max = Number.POSITIVE_INFINITY) {
35255
+ if (args.length >= min && args.length <= max) return;
35256
+ const expected = min === max ? String(min) : max === Number.POSITIVE_INFINITY ? `${min} or more` : `${min} to ${max}`;
35257
+ throw new Error(`ArgumentError: ${func} expects ${expected} argument(s).`);
35258
+ }
35091
35259
  function parseDateParts(s) {
35092
35260
  return {
35093
35261
  y: s.slice(0, 4) || "0000",
@@ -35112,6 +35280,9 @@ function applyDateDiff(date1, date22) {
35112
35280
  return String(Math.round((d1 - d2) / 864e5));
35113
35281
  }
35114
35282
  function applyDateAdd(dateStr, n, unit) {
35283
+ if (unit !== "YEAR" && unit !== "MONTH" && unit !== "DAY") {
35284
+ throw new Error("ArgumentError: DATE_ADD unit must be YEAR, MONTH, or DAY.");
35285
+ }
35115
35286
  if (!dateStr || dateStr.length < 10) return dateStr;
35116
35287
  const { y, mo, d } = parseDateParts(dateStr);
35117
35288
  const dt = new Date(Date.UTC(+y, +mo - 1, +d));
@@ -35122,7 +35293,7 @@ function applyDateAdd(dateStr, n, unit) {
35122
35293
  case "MONTH":
35123
35294
  dt.setUTCMonth(dt.getUTCMonth() + n);
35124
35295
  break;
35125
- default:
35296
+ case "DAY":
35126
35297
  dt.setUTCDate(dt.getUTCDate() + n);
35127
35298
  break;
35128
35299
  }
@@ -35131,6 +35302,15 @@ function applyDateAdd(dateStr, n, unit) {
35131
35302
  const rd = String(dt.getUTCDate()).padStart(2, "0");
35132
35303
  return `${ry}-${rmo}-${rd}`;
35133
35304
  }
35305
+ function applyLastDay(dateStr) {
35306
+ if (!dateStr || dateStr.length < 10) return dateStr;
35307
+ const { y, mo } = parseDateParts(dateStr);
35308
+ const dt = new Date(Date.UTC(+y, +mo, 0));
35309
+ const ry = String(dt.getUTCFullYear()).padStart(4, "0");
35310
+ const rmo = String(dt.getUTCMonth() + 1).padStart(2, "0");
35311
+ const rd = String(dt.getUTCDate()).padStart(2, "0");
35312
+ return `${ry}-${rmo}-${rd}`;
35313
+ }
35134
35314
  function applyFormat(num, pattern) {
35135
35315
  if (/^-?\d+$/.test(pattern.trim())) {
35136
35316
  return formatWithComma(num, Math.max(0, Number(pattern)));
@@ -36209,6 +36389,10 @@ function buildDistinctKeyBuilder(rows, columns) {
36209
36389
  values.push(row[col.field] ?? "");
36210
36390
  continue;
36211
36391
  }
36392
+ if (col.type === "WINDOW_COL") {
36393
+ values.push(row[col.alias] ?? "");
36394
+ continue;
36395
+ }
36212
36396
  if (col.type === "PARENT_WILDCARD") {
36213
36397
  for (const k of sortedParentKeys) {
36214
36398
  values.push(row[k] !== void 0 ? row[k] : null);
@@ -36220,6 +36404,9 @@ function buildDistinctKeyBuilder(rows, columns) {
36220
36404
  }
36221
36405
  function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
36222
36406
  if (orderBy.length === 0) return rows;
36407
+ return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
36408
+ }
36409
+ function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
36223
36410
  const keyMeta = orderBy.map(({ key }) => ({
36224
36411
  orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
36225
36412
  sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
@@ -36238,14 +36425,16 @@ function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
36238
36425
  };
36239
36426
  })
36240
36427
  }));
36241
- decorated.sort((a, b) => {
36242
- for (let i = 0; i < orderBy.length; i++) {
36243
- const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
36244
- if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
36245
- }
36246
- return 0;
36247
- });
36248
- return decorated.map((d) => d.row);
36428
+ const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
36429
+ decorated.sort(compare);
36430
+ return { rows: decorated, compare };
36431
+ }
36432
+ function compareDecoratedRows(a, b, orderBy, keyMeta) {
36433
+ for (let i = 0; i < orderBy.length; i++) {
36434
+ const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
36435
+ if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
36436
+ }
36437
+ return 0;
36249
36438
  }
36250
36439
  function compareSortKeys(a, b, meta3) {
36251
36440
  if (meta3.orderMap) {
@@ -36290,6 +36479,38 @@ function minChoiceIndex(values, orderMap) {
36290
36479
  }
36291
36480
  return min;
36292
36481
  }
36482
+ function applyWindow(rows, columns, optionOrders, sortKinds) {
36483
+ const windows = columns.filter((column) => column.type === "WINDOW_COL");
36484
+ if (rows.length === 0 || windows.length === 0) return rows;
36485
+ for (const window of windows) {
36486
+ const partitions = /* @__PURE__ */ new Map();
36487
+ for (const row of rows) {
36488
+ const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
36489
+ const partition = partitions.get(key);
36490
+ if (partition) partition.push(row);
36491
+ else partitions.set(key, [row]);
36492
+ }
36493
+ for (const partition of partitions.values()) {
36494
+ const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
36495
+ const sorted = sortedResult.rows;
36496
+ let rank = 1;
36497
+ let denseRank = 1;
36498
+ for (let index = 0; index < sorted.length; index++) {
36499
+ if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
36500
+ rank = index + 1;
36501
+ denseRank++;
36502
+ }
36503
+ const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
36504
+ sorted[index].row[window.alias] = String(value);
36505
+ }
36506
+ }
36507
+ }
36508
+ return rows;
36509
+ }
36510
+ function resolveWindowField(row, ref) {
36511
+ const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
36512
+ return resolveFieldRef(row, name);
36513
+ }
36293
36514
  function applyLimit(rows, limit, offset) {
36294
36515
  const start = offset ?? 0;
36295
36516
  if (limit === null) return rows.slice(start);
@@ -36380,6 +36601,12 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
36380
36601
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
36381
36602
  break;
36382
36603
  }
36604
+ case "WINDOW_COL": {
36605
+ const key = outputKeys?.[colIdx] ?? col.alias;
36606
+ out[key] = row[col.alias] ?? "";
36607
+ if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
36608
+ break;
36609
+ }
36383
36610
  }
36384
36611
  }
36385
36612
  return out;
@@ -36420,6 +36647,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
36420
36647
  return col.alias ?? stringFuncDefaultKey(col.expr);
36421
36648
  case "SCALAR_SUBQUERY_COL":
36422
36649
  return col.alias ?? "(subquery)";
36650
+ case "WINDOW_COL":
36651
+ return col.alias;
36423
36652
  case "WILDCARD":
36424
36653
  case "PARENT_WILDCARD":
36425
36654
  throw new Error("internal: computeOutputKey received a wildcard column");
@@ -36534,6 +36763,7 @@ function runFullScan(input) {
36534
36763
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
36535
36764
  }
36536
36765
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
36766
+ rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
36537
36767
  if (stmt.distinct) {
36538
36768
  rows = applyDistinct(rows, stmt.columns);
36539
36769
  }
@@ -37478,6 +37708,11 @@ function validateNoFromColumns(stmt) {
37478
37708
  throw new Error("ArgumentError: field reference is not allowed without FROM.");
37479
37709
  }
37480
37710
  break;
37711
+ case "WINDOW_COL":
37712
+ if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
37713
+ throw new Error("ArgumentError: field reference is not allowed without FROM.");
37714
+ }
37715
+ break;
37481
37716
  default:
37482
37717
  throw new Error(`ArgumentError: ${col.type} is not supported without FROM.`);
37483
37718
  }
@@ -37488,7 +37723,8 @@ function executeNoFromSelect(stmt) {
37488
37723
  throw new Error("ArgumentError: JOIN/WHERE/GROUP BY/HAVING/ORDER BY/DISTINCT are not supported without FROM.");
37489
37724
  }
37490
37725
  validateNoFromColumns(stmt);
37491
- const { rows: projected, columns } = project([{}], stmt.columns);
37726
+ const windowed = applyWindow([{}], stmt.columns);
37727
+ const { rows: projected, columns } = project(windowed, stmt.columns);
37492
37728
  const rows = applyLimit(projected, stmt.limit, stmt.offset);
37493
37729
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
37494
37730
  }
@@ -37912,6 +38148,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
37912
38148
  meta3 = { sortKind: "number" };
37913
38149
  } else if (column.type === "LITERAL_COL") {
37914
38150
  meta3 = { sortKind: "string" };
38151
+ } else if (column.type === "WINDOW_COL") {
38152
+ meta3 = { sortKind: "number" };
37915
38153
  }
37916
38154
  if (meta3) inferred.set(output, meta3);
37917
38155
  });
@@ -38497,7 +38735,10 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
38497
38735
  return map2;
38498
38736
  }
38499
38737
  async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
38500
- if (stmt.orderBy.length === 0) {
38738
+ const hasWindowOrderBy = stmt.columns.some(
38739
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
38740
+ );
38741
+ if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
38501
38742
  return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
38502
38743
  }
38503
38744
  const [optionOrders, sortKinds] = await Promise.all([
@@ -39959,6 +40200,8 @@ function collectFullScanReasons(stmt) {
39959
40200
  r.push("DISTINCT \u3042\u308A");
39960
40201
  if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))
39961
40202
  r.push("\u96C6\u8A08\u95A2\u6570\uFF08COUNT / SUM \u7B49\uFF09\u3042\u308A");
40203
+ if (stmt.columns.some((c) => c.type === "WINDOW_COL"))
40204
+ r.push("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u3042\u308A");
39962
40205
  if (stmt.columns.some((c) => c.type === "SCALAR_SUBQUERY_COL"))
39963
40206
  r.push("SELECT \u5217\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA");
39964
40207
  if (whereRequiresJsEval(stmt.where))
@@ -42414,7 +42657,7 @@ Options:
42414
42657
  -h, --help Show help
42415
42658
  `);
42416
42659
  }
42417
- var SERVER_VERSION = true ? "2.15.0" : "0.0.0-dev";
42660
+ var SERVER_VERSION = true ? "2.17.0" : "0.0.0-dev";
42418
42661
  function createServer(args) {
42419
42662
  const server = new McpServer({
42420
42663
  name: "ksql-mcp",
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rex0220/kintone-sql-tools",
3
- "version": "2.15.0",
3
+ "version": "2.17.0",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"