@rex0220/kintone-sql-tools 2.15.0 → 2.16.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 */],
@@ -464,6 +467,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
464
467
  "AVG" /* AVG */,
465
468
  "MAX" /* MAX */,
466
469
  "MIN" /* MIN */,
470
+ "ROW_NUMBER" /* ROW_NUMBER */,
471
+ "RANK" /* RANK */,
472
+ "DENSE_RANK" /* DENSE_RANK */,
467
473
  "TODAY" /* TODAY */,
468
474
  "NOW" /* NOW */,
469
475
  "LOGINUSER" /* LOGINUSER */,
@@ -982,6 +988,11 @@ var Parser = class {
982
988
  const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
983
989
  const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
984
990
  const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
991
+ const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
992
+ const hasAggregate = columns.some((column) => this.selectColumnHasAggregate(column));
993
+ if (hasWindow && (groupBy.length > 0 || hasAggregate)) {
994
+ 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());
995
+ }
985
996
  return {
986
997
  type: "SELECT",
987
998
  distinct,
@@ -1046,6 +1057,10 @@ var Parser = class {
1046
1057
  if (this.consume("*" /* STAR */)) {
1047
1058
  return { type: "WILDCARD" };
1048
1059
  }
1060
+ const windowFunc = this.tryWindowFunc();
1061
+ if (windowFunc !== null) {
1062
+ return this.parseWindowColumn(windowFunc);
1063
+ }
1049
1064
  if (this.peek().kind === "CASE" /* CASE */) {
1050
1065
  const expr = this.parseCaseWhenExpr();
1051
1066
  const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
@@ -1117,6 +1132,56 @@ var Parser = class {
1117
1132
  const alias = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
1118
1133
  return { type: "FIELD", field, alias };
1119
1134
  }
1135
+ tryWindowFunc() {
1136
+ switch (this.peek().kind) {
1137
+ case "ROW_NUMBER" /* ROW_NUMBER */:
1138
+ return "ROW_NUMBER";
1139
+ case "RANK" /* RANK */:
1140
+ return "RANK";
1141
+ case "DENSE_RANK" /* DENSE_RANK */:
1142
+ return "DENSE_RANK";
1143
+ default:
1144
+ return null;
1145
+ }
1146
+ }
1147
+ parseWindowColumn(func) {
1148
+ this.advance();
1149
+ this.expect("(" /* LPAREN */);
1150
+ if (this.peek().kind !== ")" /* RPAREN */) {
1151
+ throw new ParseError(`${func} \u306F\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u305B\u3093`, this.peek());
1152
+ }
1153
+ this.expect(")" /* RPAREN */);
1154
+ this.expectSoftKeyword("OVER", `${func} \u306B\u306F OVER (...) \u304C\u5FC5\u8981\u3067\u3059`);
1155
+ this.expect("(" /* LPAREN */);
1156
+ const partitionBy = [];
1157
+ if (this.isSoftKeyword("PARTITION")) {
1158
+ this.advance();
1159
+ this.expect("BY" /* BY */, "PARTITION \u306E\u5F8C\u306B\u306F BY \u304C\u5FC5\u8981\u3067\u3059");
1160
+ do {
1161
+ const ref = this.parseQualifiedIdent();
1162
+ partitionBy.push({ type: "FIELD", tableAlias: ref.tableAlias, field: ref.field });
1163
+ } while (this.consume("," /* COMMA */));
1164
+ }
1165
+ const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
1166
+ this.expect(")" /* RPAREN */);
1167
+ if (!this.consume("AS" /* AS */)) {
1168
+ throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
1169
+ }
1170
+ const alias = this.parseAliasName();
1171
+ return { type: "WINDOW_COL", func, partitionBy, orderBy, alias };
1172
+ }
1173
+ selectColumnHasAggregate(column) {
1174
+ if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
1175
+ if (column.type !== "STRFUNC_COL") return false;
1176
+ return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
1177
+ }
1178
+ stringFuncArgHasAggregate(arg) {
1179
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
1180
+ if (arg.type === "STRING_FUNC") {
1181
+ return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
1182
+ }
1183
+ return false;
1184
+ }
1120
1185
  isArithOp(kind) {
1121
1186
  return kind === "+" /* PLUS */ || kind === "-" /* MINUS */ || kind === "*" /* STAR */ || kind === "/" /* SLASH */ || kind === "%" /* PERCENT */;
1122
1187
  }
@@ -2898,12 +2963,16 @@ var KintoneQueryError = class extends Error {
2898
2963
  };
2899
2964
 
2900
2965
  // src/converter/selectToKintone.ts
2966
+ function hasWindowColumns(columns) {
2967
+ return columns.some((column) => column.type === "WINDOW_COL");
2968
+ }
2901
2969
  function resolveSelectMode(stmt) {
2902
2970
  if (stmt.from.subtableCode) return "FULL_SCAN";
2903
2971
  if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
2904
2972
  if (stmt.joins.length > 0) return "FULL_SCAN";
2905
2973
  if (stmt.groupBy.length > 0) return "FULL_SCAN";
2906
2974
  if (stmt.distinct) return "FULL_SCAN";
2975
+ if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
2907
2976
  if (stmt.columns.some(
2908
2977
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
2909
2978
  )) return "FULL_SCAN";
@@ -3275,16 +3344,16 @@ function collectRequiredFieldsByTable(stmt) {
3275
3344
  }
3276
3345
  walkStringFunc(k.expr, "groupBy");
3277
3346
  };
3278
- const walkOrderByKey = (k) => {
3347
+ const walkOrderByKey = (k, phase = "orderBy") => {
3279
3348
  if (k.type === "FIELD_NAME") {
3280
- addFieldName(k.name, "orderBy");
3349
+ addFieldName(k.name, phase);
3281
3350
  return;
3282
3351
  }
3283
3352
  if (k.type === "ARITH_KEY") {
3284
- walkArith(k.expr, "orderBy");
3353
+ walkArith(k.expr, phase);
3285
3354
  return;
3286
3355
  }
3287
- walkStringFunc(k.expr, "orderBy");
3356
+ walkStringFunc(k.expr, phase);
3288
3357
  };
3289
3358
  for (const col of stmt.columns) {
3290
3359
  switch (col.type) {
@@ -3316,6 +3385,10 @@ function collectRequiredFieldsByTable(stmt) {
3316
3385
  break;
3317
3386
  case "SCALAR_SUBQUERY_COL":
3318
3387
  break;
3388
+ case "WINDOW_COL":
3389
+ for (const ref of col.partitionBy) addFieldRef(ref.field, ref.tableAlias, "select");
3390
+ for (const item of col.orderBy) walkOrderByKey(item.key, "select");
3391
+ break;
3319
3392
  }
3320
3393
  }
3321
3394
  for (const join2 of stmt.joins) {
@@ -3362,6 +3435,10 @@ function collectSelectOutputNames(columns) {
3362
3435
  }
3363
3436
  if (col.type === "SCALAR_SUBQUERY_COL") {
3364
3437
  names.add(col.alias ?? "(subquery)");
3438
+ continue;
3439
+ }
3440
+ if (col.type === "WINDOW_COL") {
3441
+ names.add(col.alias);
3365
3442
  }
3366
3443
  }
3367
3444
  return names;
@@ -5309,6 +5386,10 @@ function buildDistinctKeyBuilder(rows, columns) {
5309
5386
  values.push(row[col.field] ?? "");
5310
5387
  continue;
5311
5388
  }
5389
+ if (col.type === "WINDOW_COL") {
5390
+ values.push(row[col.alias] ?? "");
5391
+ continue;
5392
+ }
5312
5393
  if (col.type === "PARENT_WILDCARD") {
5313
5394
  for (const k of sortedParentKeys) {
5314
5395
  values.push(row[k] !== void 0 ? row[k] : null);
@@ -5320,6 +5401,9 @@ function buildDistinctKeyBuilder(rows, columns) {
5320
5401
  }
5321
5402
  function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
5322
5403
  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) {
5323
5407
  const keyMeta = orderBy.map(({ key }) => ({
5324
5408
  orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
5325
5409
  sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
@@ -5338,14 +5422,16 @@ function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
5338
5422
  };
5339
5423
  })
5340
5424
  }));
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);
5425
+ const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
5426
+ decorated.sort(compare);
5427
+ return { rows: decorated, compare };
5428
+ }
5429
+ function compareDecoratedRows(a, b, orderBy, keyMeta) {
5430
+ for (let i = 0; i < orderBy.length; i++) {
5431
+ const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
5432
+ if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
5433
+ }
5434
+ return 0;
5349
5435
  }
5350
5436
  function compareSortKeys(a, b, meta) {
5351
5437
  if (meta.orderMap) {
@@ -5390,6 +5476,38 @@ function minChoiceIndex(values, orderMap) {
5390
5476
  }
5391
5477
  return min;
5392
5478
  }
5479
+ function applyWindow(rows, columns, optionOrders, sortKinds) {
5480
+ const windows = columns.filter((column) => column.type === "WINDOW_COL");
5481
+ if (rows.length === 0 || windows.length === 0) return rows;
5482
+ for (const window of windows) {
5483
+ const partitions = /* @__PURE__ */ new Map();
5484
+ for (const row of rows) {
5485
+ const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
5486
+ const partition = partitions.get(key);
5487
+ if (partition) partition.push(row);
5488
+ else partitions.set(key, [row]);
5489
+ }
5490
+ for (const partition of partitions.values()) {
5491
+ const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
5492
+ const sorted = sortedResult.rows;
5493
+ let rank = 1;
5494
+ let denseRank = 1;
5495
+ for (let index = 0; index < sorted.length; index++) {
5496
+ if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
5497
+ rank = index + 1;
5498
+ denseRank++;
5499
+ }
5500
+ const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
5501
+ sorted[index].row[window.alias] = String(value);
5502
+ }
5503
+ }
5504
+ }
5505
+ return rows;
5506
+ }
5507
+ function resolveWindowField(row, ref) {
5508
+ const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
5509
+ return resolveFieldRef(row, name);
5510
+ }
5393
5511
  function applyLimit(rows, limit, offset) {
5394
5512
  const start = offset ?? 0;
5395
5513
  if (limit === null) return rows.slice(start);
@@ -5480,6 +5598,12 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
5480
5598
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
5481
5599
  break;
5482
5600
  }
5601
+ case "WINDOW_COL": {
5602
+ const key = outputKeys?.[colIdx] ?? col.alias;
5603
+ out[key] = row[col.alias] ?? "";
5604
+ if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
5605
+ break;
5606
+ }
5483
5607
  }
5484
5608
  }
5485
5609
  return out;
@@ -5520,6 +5644,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
5520
5644
  return col.alias ?? stringFuncDefaultKey(col.expr);
5521
5645
  case "SCALAR_SUBQUERY_COL":
5522
5646
  return col.alias ?? "(subquery)";
5647
+ case "WINDOW_COL":
5648
+ return col.alias;
5523
5649
  case "WILDCARD":
5524
5650
  case "PARENT_WILDCARD":
5525
5651
  throw new Error("internal: computeOutputKey received a wildcard column");
@@ -5634,6 +5760,7 @@ function runFullScan(input) {
5634
5760
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
5635
5761
  }
5636
5762
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
5763
+ rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
5637
5764
  if (stmt.distinct) {
5638
5765
  rows = applyDistinct(rows, stmt.columns);
5639
5766
  }
@@ -6578,6 +6705,11 @@ function validateNoFromColumns(stmt) {
6578
6705
  throw new Error("ArgumentError: field reference is not allowed without FROM.");
6579
6706
  }
6580
6707
  break;
6708
+ case "WINDOW_COL":
6709
+ if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
6710
+ throw new Error("ArgumentError: field reference is not allowed without FROM.");
6711
+ }
6712
+ break;
6581
6713
  default:
6582
6714
  throw new Error(`ArgumentError: ${col.type} is not supported without FROM.`);
6583
6715
  }
@@ -6588,7 +6720,8 @@ function executeNoFromSelect(stmt) {
6588
6720
  throw new Error("ArgumentError: JOIN/WHERE/GROUP BY/HAVING/ORDER BY/DISTINCT are not supported without FROM.");
6589
6721
  }
6590
6722
  validateNoFromColumns(stmt);
6591
- const { rows: projected, columns } = project([{}], stmt.columns);
6723
+ const windowed = applyWindow([{}], stmt.columns);
6724
+ const { rows: projected, columns } = project(windowed, stmt.columns);
6592
6725
  const rows = applyLimit(projected, stmt.limit, stmt.offset);
6593
6726
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
6594
6727
  }
@@ -7012,6 +7145,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
7012
7145
  meta = { sortKind: "number" };
7013
7146
  } else if (column.type === "LITERAL_COL") {
7014
7147
  meta = { sortKind: "string" };
7148
+ } else if (column.type === "WINDOW_COL") {
7149
+ meta = { sortKind: "number" };
7015
7150
  }
7016
7151
  if (meta) inferred.set(output, meta);
7017
7152
  });
@@ -7597,7 +7732,10 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
7597
7732
  return map;
7598
7733
  }
7599
7734
  async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
7600
- if (stmt.orderBy.length === 0) {
7735
+ const hasWindowOrderBy = stmt.columns.some(
7736
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
7737
+ );
7738
+ if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
7601
7739
  return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
7602
7740
  }
7603
7741
  const [optionOrders, sortKinds] = await Promise.all([
@@ -9059,6 +9197,8 @@ function collectFullScanReasons(stmt) {
9059
9197
  r.push("DISTINCT \u3042\u308A");
9060
9198
  if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))
9061
9199
  r.push("\u96C6\u8A08\u95A2\u6570\uFF08COUNT / SUM \u7B49\uFF09\u3042\u308A");
9200
+ if (stmt.columns.some((c) => c.type === "WINDOW_COL"))
9201
+ r.push("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u3042\u308A");
9062
9202
  if (stmt.columns.some((c) => c.type === "SCALAR_SUBQUERY_COL"))
9063
9203
  r.push("SELECT \u5217\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA");
9064
9204
  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 */],
@@ -31376,6 +31379,9 @@ var FUNC_CALL_PREFIX_KINDS = /* @__PURE__ */ new Set([
31376
31379
  "AVG" /* AVG */,
31377
31380
  "MAX" /* MAX */,
31378
31381
  "MIN" /* MIN */,
31382
+ "ROW_NUMBER" /* ROW_NUMBER */,
31383
+ "RANK" /* RANK */,
31384
+ "DENSE_RANK" /* DENSE_RANK */,
31379
31385
  "TODAY" /* TODAY */,
31380
31386
  "NOW" /* NOW */,
31381
31387
  "LOGINUSER" /* LOGINUSER */,
@@ -31894,6 +31900,11 @@ var Parser = class {
31894
31900
  const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
31895
31901
  const limit = this.consume("LIMIT" /* LIMIT */) ? this.parseUnsignedInt() : null;
31896
31902
  const offset = this.consume("OFFSET" /* OFFSET */) ? this.parseUnsignedInt() : null;
31903
+ const hasWindow = columns.some((column) => column.type === "WINDOW_COL");
31904
+ const hasAggregate = columns.some((column) => this.selectColumnHasAggregate(column));
31905
+ if (hasWindow && (groupBy.length > 0 || hasAggregate)) {
31906
+ 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());
31907
+ }
31897
31908
  return {
31898
31909
  type: "SELECT",
31899
31910
  distinct,
@@ -31958,6 +31969,10 @@ var Parser = class {
31958
31969
  if (this.consume("*" /* STAR */)) {
31959
31970
  return { type: "WILDCARD" };
31960
31971
  }
31972
+ const windowFunc = this.tryWindowFunc();
31973
+ if (windowFunc !== null) {
31974
+ return this.parseWindowColumn(windowFunc);
31975
+ }
31961
31976
  if (this.peek().kind === "CASE" /* CASE */) {
31962
31977
  const expr = this.parseCaseWhenExpr();
31963
31978
  const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
@@ -32029,6 +32044,56 @@ var Parser = class {
32029
32044
  const alias = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
32030
32045
  return { type: "FIELD", field, alias };
32031
32046
  }
32047
+ tryWindowFunc() {
32048
+ switch (this.peek().kind) {
32049
+ case "ROW_NUMBER" /* ROW_NUMBER */:
32050
+ return "ROW_NUMBER";
32051
+ case "RANK" /* RANK */:
32052
+ return "RANK";
32053
+ case "DENSE_RANK" /* DENSE_RANK */:
32054
+ return "DENSE_RANK";
32055
+ default:
32056
+ return null;
32057
+ }
32058
+ }
32059
+ parseWindowColumn(func) {
32060
+ this.advance();
32061
+ this.expect("(" /* LPAREN */);
32062
+ if (this.peek().kind !== ")" /* RPAREN */) {
32063
+ throw new ParseError(`${func} \u306F\u5F15\u6570\u3092\u53D7\u3051\u4ED8\u3051\u307E\u305B\u3093`, this.peek());
32064
+ }
32065
+ this.expect(")" /* RPAREN */);
32066
+ this.expectSoftKeyword("OVER", `${func} \u306B\u306F OVER (...) \u304C\u5FC5\u8981\u3067\u3059`);
32067
+ this.expect("(" /* LPAREN */);
32068
+ const partitionBy = [];
32069
+ if (this.isSoftKeyword("PARTITION")) {
32070
+ this.advance();
32071
+ this.expect("BY" /* BY */, "PARTITION \u306E\u5F8C\u306B\u306F BY \u304C\u5FC5\u8981\u3067\u3059");
32072
+ do {
32073
+ const ref = this.parseQualifiedIdent();
32074
+ partitionBy.push({ type: "FIELD", tableAlias: ref.tableAlias, field: ref.field });
32075
+ } while (this.consume("," /* COMMA */));
32076
+ }
32077
+ const orderBy = this.consume("ORDER" /* ORDER */) ? (this.expect("BY" /* BY */), this.parseOrderBy()) : [];
32078
+ this.expect(")" /* RPAREN */);
32079
+ if (!this.consume("AS" /* AS */)) {
32080
+ throw new ParseError("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u306B\u306F AS alias \u304C\u5FC5\u8981\u3067\u3059", this.peek());
32081
+ }
32082
+ const alias = this.parseAliasName();
32083
+ return { type: "WINDOW_COL", func, partitionBy, orderBy, alias };
32084
+ }
32085
+ selectColumnHasAggregate(column) {
32086
+ if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
32087
+ if (column.type !== "STRFUNC_COL") return false;
32088
+ return column.expr.args.some((arg) => this.stringFuncArgHasAggregate(arg));
32089
+ }
32090
+ stringFuncArgHasAggregate(arg) {
32091
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
32092
+ if (arg.type === "STRING_FUNC") {
32093
+ return arg.args.some((nested) => this.stringFuncArgHasAggregate(nested));
32094
+ }
32095
+ return false;
32096
+ }
32032
32097
  isArithOp(kind) {
32033
32098
  return kind === "+" /* PLUS */ || kind === "-" /* MINUS */ || kind === "*" /* STAR */ || kind === "/" /* SLASH */ || kind === "%" /* PERCENT */;
32034
32099
  }
@@ -33798,12 +33863,16 @@ var KintoneQueryError = class extends Error {
33798
33863
  };
33799
33864
 
33800
33865
  // src/converter/selectToKintone.ts
33866
+ function hasWindowColumns(columns) {
33867
+ return columns.some((column) => column.type === "WINDOW_COL");
33868
+ }
33801
33869
  function resolveSelectMode(stmt) {
33802
33870
  if (stmt.from.subtableCode) return "FULL_SCAN";
33803
33871
  if (stmt.joins.some((j) => j.table.subtableCode)) return "FULL_SCAN";
33804
33872
  if (stmt.joins.length > 0) return "FULL_SCAN";
33805
33873
  if (stmt.groupBy.length > 0) return "FULL_SCAN";
33806
33874
  if (stmt.distinct) return "FULL_SCAN";
33875
+ if (hasWindowColumns(stmt.columns)) return "FULL_SCAN";
33807
33876
  if (stmt.columns.some(
33808
33877
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "SCALAR_SUBQUERY_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr(c.expr)
33809
33878
  )) return "FULL_SCAN";
@@ -34175,16 +34244,16 @@ function collectRequiredFieldsByTable(stmt) {
34175
34244
  }
34176
34245
  walkStringFunc(k.expr, "groupBy");
34177
34246
  };
34178
- const walkOrderByKey = (k) => {
34247
+ const walkOrderByKey = (k, phase = "orderBy") => {
34179
34248
  if (k.type === "FIELD_NAME") {
34180
- addFieldName(k.name, "orderBy");
34249
+ addFieldName(k.name, phase);
34181
34250
  return;
34182
34251
  }
34183
34252
  if (k.type === "ARITH_KEY") {
34184
- walkArith(k.expr, "orderBy");
34253
+ walkArith(k.expr, phase);
34185
34254
  return;
34186
34255
  }
34187
- walkStringFunc(k.expr, "orderBy");
34256
+ walkStringFunc(k.expr, phase);
34188
34257
  };
34189
34258
  for (const col of stmt.columns) {
34190
34259
  switch (col.type) {
@@ -34216,6 +34285,10 @@ function collectRequiredFieldsByTable(stmt) {
34216
34285
  break;
34217
34286
  case "SCALAR_SUBQUERY_COL":
34218
34287
  break;
34288
+ case "WINDOW_COL":
34289
+ for (const ref of col.partitionBy) addFieldRef(ref.field, ref.tableAlias, "select");
34290
+ for (const item of col.orderBy) walkOrderByKey(item.key, "select");
34291
+ break;
34219
34292
  }
34220
34293
  }
34221
34294
  for (const join of stmt.joins) {
@@ -34262,6 +34335,10 @@ function collectSelectOutputNames(columns) {
34262
34335
  }
34263
34336
  if (col.type === "SCALAR_SUBQUERY_COL") {
34264
34337
  names.add(col.alias ?? "(subquery)");
34338
+ continue;
34339
+ }
34340
+ if (col.type === "WINDOW_COL") {
34341
+ names.add(col.alias);
34265
34342
  }
34266
34343
  }
34267
34344
  return names;
@@ -36209,6 +36286,10 @@ function buildDistinctKeyBuilder(rows, columns) {
36209
36286
  values.push(row[col.field] ?? "");
36210
36287
  continue;
36211
36288
  }
36289
+ if (col.type === "WINDOW_COL") {
36290
+ values.push(row[col.alias] ?? "");
36291
+ continue;
36292
+ }
36212
36293
  if (col.type === "PARENT_WILDCARD") {
36213
36294
  for (const k of sortedParentKeys) {
36214
36295
  values.push(row[k] !== void 0 ? row[k] : null);
@@ -36220,6 +36301,9 @@ function buildDistinctKeyBuilder(rows, columns) {
36220
36301
  }
36221
36302
  function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
36222
36303
  if (orderBy.length === 0) return rows;
36304
+ return sortDecoratedRows(rows, orderBy, optionOrders, sortKinds).rows.map((item) => item.row);
36305
+ }
36306
+ function sortDecoratedRows(rows, orderBy, optionOrders, sortKinds) {
36223
36307
  const keyMeta = orderBy.map(({ key }) => ({
36224
36308
  orderMap: key.type === "FIELD_NAME" ? optionOrders?.get(key.name) : void 0,
36225
36309
  sortKind: key.type === "FIELD_NAME" ? sortKinds?.get(key.name) : void 0
@@ -36238,14 +36322,16 @@ function applyOrderBy(rows, orderBy, optionOrders, sortKinds) {
36238
36322
  };
36239
36323
  })
36240
36324
  }));
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);
36325
+ const compare = (a, b) => compareDecoratedRows(a, b, orderBy, keyMeta);
36326
+ decorated.sort(compare);
36327
+ return { rows: decorated, compare };
36328
+ }
36329
+ function compareDecoratedRows(a, b, orderBy, keyMeta) {
36330
+ for (let i = 0; i < orderBy.length; i++) {
36331
+ const cmp = compareSortKeys(a.keys[i], b.keys[i], keyMeta[i]);
36332
+ if (cmp !== 0) return orderBy[i].direction === "ASC" ? cmp : -cmp;
36333
+ }
36334
+ return 0;
36249
36335
  }
36250
36336
  function compareSortKeys(a, b, meta3) {
36251
36337
  if (meta3.orderMap) {
@@ -36290,6 +36376,38 @@ function minChoiceIndex(values, orderMap) {
36290
36376
  }
36291
36377
  return min;
36292
36378
  }
36379
+ function applyWindow(rows, columns, optionOrders, sortKinds) {
36380
+ const windows = columns.filter((column) => column.type === "WINDOW_COL");
36381
+ if (rows.length === 0 || windows.length === 0) return rows;
36382
+ for (const window of windows) {
36383
+ const partitions = /* @__PURE__ */ new Map();
36384
+ for (const row of rows) {
36385
+ const key = JSON.stringify(window.partitionBy.map((ref) => resolveWindowField(row, ref)));
36386
+ const partition = partitions.get(key);
36387
+ if (partition) partition.push(row);
36388
+ else partitions.set(key, [row]);
36389
+ }
36390
+ for (const partition of partitions.values()) {
36391
+ const sortedResult = sortDecoratedRows(partition, window.orderBy, optionOrders, sortKinds);
36392
+ const sorted = sortedResult.rows;
36393
+ let rank = 1;
36394
+ let denseRank = 1;
36395
+ for (let index = 0; index < sorted.length; index++) {
36396
+ if (index > 0 && sortedResult.compare(sorted[index - 1], sorted[index]) !== 0) {
36397
+ rank = index + 1;
36398
+ denseRank++;
36399
+ }
36400
+ const value = window.func === "ROW_NUMBER" ? index + 1 : window.func === "RANK" ? rank : denseRank;
36401
+ sorted[index].row[window.alias] = String(value);
36402
+ }
36403
+ }
36404
+ }
36405
+ return rows;
36406
+ }
36407
+ function resolveWindowField(row, ref) {
36408
+ const name = ref.tableAlias ? `${ref.tableAlias}.${ref.field}` : ref.field;
36409
+ return resolveFieldRef(row, name);
36410
+ }
36293
36411
  function applyLimit(rows, limit, offset) {
36294
36412
  const start = offset ?? 0;
36295
36413
  if (limit === null) return rows.slice(start);
@@ -36380,6 +36498,12 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns) {
36380
36498
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
36381
36499
  break;
36382
36500
  }
36501
+ case "WINDOW_COL": {
36502
+ const key = outputKeys?.[colIdx] ?? col.alias;
36503
+ out[key] = row[col.alias] ?? "";
36504
+ if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
36505
+ break;
36506
+ }
36383
36507
  }
36384
36508
  }
36385
36509
  return out;
@@ -36420,6 +36544,8 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
36420
36544
  return col.alias ?? stringFuncDefaultKey(col.expr);
36421
36545
  case "SCALAR_SUBQUERY_COL":
36422
36546
  return col.alias ?? "(subquery)";
36547
+ case "WINDOW_COL":
36548
+ return col.alias;
36423
36549
  case "WILDCARD":
36424
36550
  case "PARENT_WILDCARD":
36425
36551
  throw new Error("internal: computeOutputKey received a wildcard column");
@@ -36534,6 +36660,7 @@ function runFullScan(input) {
36534
36660
  rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
36535
36661
  }
36536
36662
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
36663
+ rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds);
36537
36664
  if (stmt.distinct) {
36538
36665
  rows = applyDistinct(rows, stmt.columns);
36539
36666
  }
@@ -37478,6 +37605,11 @@ function validateNoFromColumns(stmt) {
37478
37605
  throw new Error("ArgumentError: field reference is not allowed without FROM.");
37479
37606
  }
37480
37607
  break;
37608
+ case "WINDOW_COL":
37609
+ if (col.partitionBy.length > 0 || col.orderBy.length > 0) {
37610
+ throw new Error("ArgumentError: field reference is not allowed without FROM.");
37611
+ }
37612
+ break;
37481
37613
  default:
37482
37614
  throw new Error(`ArgumentError: ${col.type} is not supported without FROM.`);
37483
37615
  }
@@ -37488,7 +37620,8 @@ function executeNoFromSelect(stmt) {
37488
37620
  throw new Error("ArgumentError: JOIN/WHERE/GROUP BY/HAVING/ORDER BY/DISTINCT are not supported without FROM.");
37489
37621
  }
37490
37622
  validateNoFromColumns(stmt);
37491
- const { rows: projected, columns } = project([{}], stmt.columns);
37623
+ const windowed = applyWindow([{}], stmt.columns);
37624
+ const { rows: projected, columns } = project(windowed, stmt.columns);
37492
37625
  const rows = applyLimit(projected, stmt.limit, stmt.offset);
37493
37626
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [] };
37494
37627
  }
@@ -37912,6 +38045,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
37912
38045
  meta3 = { sortKind: "number" };
37913
38046
  } else if (column.type === "LITERAL_COL") {
37914
38047
  meta3 = { sortKind: "string" };
38048
+ } else if (column.type === "WINDOW_COL") {
38049
+ meta3 = { sortKind: "number" };
37915
38050
  }
37916
38051
  if (meta3) inferred.set(output, meta3);
37917
38052
  });
@@ -38497,7 +38632,10 @@ async function getSortKindMapByApp(appId, client, cacheContext) {
38497
38632
  return map2;
38498
38633
  }
38499
38634
  async function buildOrderByMetaForSelect(stmt, client, cacheContext) {
38500
- if (stmt.orderBy.length === 0) {
38635
+ const hasWindowOrderBy = stmt.columns.some(
38636
+ (column) => column.type === "WINDOW_COL" && column.orderBy.length > 0
38637
+ );
38638
+ if (stmt.orderBy.length === 0 && !hasWindowOrderBy) {
38501
38639
  return { optionOrders: /* @__PURE__ */ new Map(), sortKinds: /* @__PURE__ */ new Map() };
38502
38640
  }
38503
38641
  const [optionOrders, sortKinds] = await Promise.all([
@@ -39959,6 +40097,8 @@ function collectFullScanReasons(stmt) {
39959
40097
  r.push("DISTINCT \u3042\u308A");
39960
40098
  if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))
39961
40099
  r.push("\u96C6\u8A08\u95A2\u6570\uFF08COUNT / SUM \u7B49\uFF09\u3042\u308A");
40100
+ if (stmt.columns.some((c) => c.type === "WINDOW_COL"))
40101
+ r.push("\u30A6\u30A3\u30F3\u30C9\u30A6\u95A2\u6570\u3042\u308A");
39962
40102
  if (stmt.columns.some((c) => c.type === "SCALAR_SUBQUERY_COL"))
39963
40103
  r.push("SELECT \u5217\u306B\u30B9\u30AB\u30E9\u30FC\u30B5\u30D6\u30AF\u30A8\u30EA");
39964
40104
  if (whereRequiresJsEval(stmt.where))
@@ -42414,7 +42554,7 @@ Options:
42414
42554
  -h, --help Show help
42415
42555
  `);
42416
42556
  }
42417
- var SERVER_VERSION = true ? "2.15.0" : "0.0.0-dev";
42557
+ var SERVER_VERSION = true ? "2.16.0" : "0.0.0-dev";
42418
42558
  function createServer(args) {
42419
42559
  const server = new McpServer({
42420
42560
  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.16.0",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"