@rex0220/kintone-sql-tools 2.13.0 → 2.14.1

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
@@ -1842,16 +1842,25 @@ var Parser = class {
1842
1842
  // IN リストの値
1843
1843
  parseInValues() {
1844
1844
  const values = [];
1845
+ const invalidValueMessage = "IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u3001\u6570\u5024\u3001\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059";
1845
1846
  do {
1846
1847
  const tok = this.advance();
1847
1848
  if (tok.kind === "STRING" /* STRING */) {
1848
1849
  values.push({ type: "STRING", value: tok.value });
1849
1850
  } else if (tok.kind === "NUMBER" /* NUMBER */) {
1850
1851
  values.push({ type: "NUMBER", value: Number(tok.value) });
1852
+ } else if (tok.kind === "-" /* MINUS */ || tok.kind === "+" /* PLUS */) {
1853
+ const number = this.peek();
1854
+ if (number.kind !== "NUMBER" /* NUMBER */) {
1855
+ throw new ParseError(invalidValueMessage, tok);
1856
+ }
1857
+ this.advance();
1858
+ const sign = tok.kind === "-" /* MINUS */ ? -1 : 1;
1859
+ values.push({ type: "NUMBER", value: sign * Number(number.value) });
1851
1860
  } else if (tok.kind === "VARIABLE" /* VARIABLE */) {
1852
1861
  values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
1853
1862
  } else {
1854
- throw new ParseError("IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u3001\u6570\u5024\u3001\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059", tok);
1863
+ throw new ParseError(invalidValueMessage, tok);
1855
1864
  }
1856
1865
  } while (this.consume("," /* COMMA */));
1857
1866
  return values;
@@ -5090,7 +5099,7 @@ function hasAggregateColumns(columns) {
5090
5099
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
5091
5100
  );
5092
5101
  }
5093
- function applyGroupBy(rows, groupByKeys, columns) {
5102
+ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
5094
5103
  const groups = /* @__PURE__ */ new Map();
5095
5104
  for (const row of rows) {
5096
5105
  const key = groupByKeys.map((k) => evalGroupByKey(k, row)).join("\0");
@@ -5114,15 +5123,15 @@ function applyGroupBy(rows, groupByKeys, columns) {
5114
5123
  for (const col of columns) {
5115
5124
  if (col.type === "AGGREGATE") {
5116
5125
  const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
5117
- const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows));
5126
+ const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows, resolveAggSortKind));
5118
5127
  outRow[col.alias ?? syntheticKey] = value;
5119
5128
  if (col.alias) outRow[syntheticKey] = value;
5120
5129
  } else if (col.type === "ARITH_AGG_COL") {
5121
5130
  const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
5122
- outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows));
5131
+ outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows, resolveAggSortKind));
5123
5132
  } else if (col.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(col.expr)) {
5124
5133
  const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
5125
- const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows);
5134
+ const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
5126
5135
  outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
5127
5136
  }
5128
5137
  }
@@ -5135,7 +5144,7 @@ function evalGroupByKey(key, row) {
5135
5144
  if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
5136
5145
  return String(evalArithExpr(key.expr, row));
5137
5146
  }
5138
- function evalAggregate(func, distinct, arg, rows) {
5147
+ function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
5139
5148
  if (arg.type === "WILDCARD") {
5140
5149
  return func === "COUNT" ? rows.length : 0;
5141
5150
  }
@@ -5155,6 +5164,11 @@ function evalAggregate(func, distinct, arg, rows) {
5155
5164
  }
5156
5165
  const eff = distinct ? [...new Set(strValues)] : strValues;
5157
5166
  if (func === "COUNT") return eff.length;
5167
+ const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
5168
+ if (sortKind === "string") {
5169
+ if (eff.length === 0) return "";
5170
+ return func === "MAX" ? maxStringOf(eff) : minStringOf(eff);
5171
+ }
5158
5172
  const nums = eff.map(Number);
5159
5173
  switch (func) {
5160
5174
  case "SUM":
@@ -5168,6 +5182,20 @@ function evalAggregate(func, distinct, arg, rows) {
5168
5182
  return nums.length === 0 ? 0 : minOf(nums);
5169
5183
  }
5170
5184
  }
5185
+ function toAggregateFieldRef(field) {
5186
+ const dot = field.indexOf(".");
5187
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
5188
+ }
5189
+ function maxStringOf(values) {
5190
+ let value = values[0];
5191
+ for (const candidate of values) if (candidate > value) value = candidate;
5192
+ return value;
5193
+ }
5194
+ function minStringOf(values) {
5195
+ let value = values[0];
5196
+ for (const candidate of values) if (candidate < value) value = candidate;
5197
+ return value;
5198
+ }
5171
5199
  function maxOf(nums) {
5172
5200
  let m = nums[0];
5173
5201
  for (const n of nums) if (n > m) m = n;
@@ -5178,11 +5206,11 @@ function minOf(nums) {
5178
5206
  for (const n of nums) if (n < m) m = n;
5179
5207
  return m;
5180
5208
  }
5181
- function evalAggArithExpr(node, rows) {
5209
+ function evalAggArithExpr(node, rows, resolveAggSortKind) {
5182
5210
  if (node.type === "NUMBER") return node.value;
5183
- if (node.type === "AGG_REF") return evalAggregate(node.func, node.distinct, node.arg, rows);
5184
- const l = evalAggArithExpr(node.left, rows);
5185
- const r = evalAggArithExpr(node.right, rows);
5211
+ if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, rows, resolveAggSortKind));
5212
+ const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
5213
+ const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
5186
5214
  switch (node.op) {
5187
5215
  case "+":
5188
5216
  return l + r;
@@ -5527,26 +5555,24 @@ function hasAggregateInStringFuncArg(arg) {
5527
5555
  function hasAggregateInStringFuncExpr2(expr) {
5528
5556
  return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
5529
5557
  }
5530
- function resolveAggInStringFuncArg(arg, rows) {
5558
+ function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
5531
5559
  if (arg.type === "AGG_REF") {
5532
- return {
5533
- type: "NUMBER",
5534
- value: evalAggregate(arg.func, arg.distinct, arg.arg, rows)
5535
- };
5560
+ const value = evalAggregate(arg.func, arg.distinct, arg.arg, rows, resolveAggSortKind);
5561
+ return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
5536
5562
  }
5537
5563
  if (arg.type === "AGG_ARITH") {
5538
- return { type: "NUMBER", value: evalAggArithExpr(arg, rows) };
5564
+ return { type: "NUMBER", value: evalAggArithExpr(arg, rows, resolveAggSortKind) };
5539
5565
  }
5540
5566
  if (arg.type === "STRING_FUNC") {
5541
- return resolveAggInStringFuncExpr(arg, rows);
5567
+ return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
5542
5568
  }
5543
5569
  return arg;
5544
5570
  }
5545
- function resolveAggInStringFuncExpr(expr, rows) {
5571
+ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
5546
5572
  return {
5547
5573
  type: "STRING_FUNC",
5548
5574
  func: expr.func,
5549
- args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows))
5575
+ args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
5550
5576
  };
5551
5577
  }
5552
5578
  function runFullScan(input) {
@@ -5558,6 +5584,7 @@ function runFullScan(input) {
5558
5584
  sortKinds,
5559
5585
  fieldTypeResolver,
5560
5586
  havingFieldTypeResolver,
5587
+ aggregateSortKindResolver,
5561
5588
  appliedKlikes,
5562
5589
  sourceColumns
5563
5590
  } = input;
@@ -5573,7 +5600,7 @@ function runFullScan(input) {
5573
5600
  }
5574
5601
  rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
5575
5602
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
5576
- rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
5603
+ rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
5577
5604
  }
5578
5605
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
5579
5606
  if (stmt.distinct) {
@@ -6722,6 +6749,116 @@ async function loadTypedInFieldTypes(stmt, client, cacheContext) {
6722
6749
  const entries = await Promise.all([...appIds].map(async (appId) => [appId, await getFieldTypeMap(appId, client, cacheContext)]));
6723
6750
  return new Map(entries);
6724
6751
  }
6752
+ function aggregateFieldRef(field) {
6753
+ const dot = field.indexOf(".");
6754
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
6755
+ }
6756
+ function collectAggregateRef(func, arg, out) {
6757
+ if ((func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" && arg.field) {
6758
+ out.push(aggregateFieldRef(arg.field));
6759
+ }
6760
+ }
6761
+ function collectAggregateOperandRefs(node, out) {
6762
+ if (node.type === "AGG_REF") {
6763
+ collectAggregateRef(node.func, node.arg, out);
6764
+ return;
6765
+ }
6766
+ if (node.type === "AGG_ARITH") {
6767
+ collectAggregateOperandRefs(node.left, out);
6768
+ collectAggregateOperandRefs(node.right, out);
6769
+ }
6770
+ }
6771
+ function collectStringFuncAggregateRefs(expr, out) {
6772
+ for (const arg of expr.args) {
6773
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
6774
+ collectAggregateOperandRefs(arg, out);
6775
+ } else if (arg.type === "STRING_FUNC") {
6776
+ collectStringFuncAggregateRefs(arg, out);
6777
+ }
6778
+ }
6779
+ }
6780
+ function collectSelectAggregateSortRefs(columns) {
6781
+ const refs = [];
6782
+ for (const column of columns) {
6783
+ if (column.type === "AGGREGATE") {
6784
+ collectAggregateRef(column.func, column.arg, refs);
6785
+ } else if (column.type === "ARITH_AGG_COL") {
6786
+ collectAggregateOperandRefs(column.expr, refs);
6787
+ } else if (column.type === "STRFUNC_COL") {
6788
+ collectStringFuncAggregateRefs(column.expr, refs);
6789
+ }
6790
+ }
6791
+ return refs;
6792
+ }
6793
+ var AGGREGATE_STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
6794
+ "SINGLE_LINE_TEXT",
6795
+ "MULTI_LINE_TEXT",
6796
+ "RICH_TEXT",
6797
+ "LINK",
6798
+ "DROP_DOWN",
6799
+ "RADIO_BUTTON",
6800
+ "STATUS",
6801
+ "DATE",
6802
+ "TIME",
6803
+ "DATETIME",
6804
+ "CREATED_TIME",
6805
+ "UPDATED_TIME"
6806
+ ]);
6807
+ function aggregateSortKind(info) {
6808
+ if (info.sortKind !== void 0) return info.sortKind;
6809
+ if (info.fieldType === "NUMBER" || info.fieldType === "RECORD_NUMBER") return "number";
6810
+ return AGGREGATE_STRING_FIELD_TYPES.has(info.fieldType) ? "string" : void 0;
6811
+ }
6812
+ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
6813
+ const refs = collectSelectAggregateSortRefs(stmt.columns);
6814
+ if (refs.length === 0) return void 0;
6815
+ const appIds = /* @__PURE__ */ new Set();
6816
+ const physicalTables = physicalSelectTables(stmt);
6817
+ for (const ref of refs) {
6818
+ if (ref.tableAlias !== null) {
6819
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
6820
+ appIds.add(stmt.from.appId);
6821
+ continue;
6822
+ }
6823
+ const table = findTableForAlias(stmt, ref.tableAlias);
6824
+ if (table && table.cteName === null) appIds.add(table.appId);
6825
+ } else if (stmt.joins.length === 0) {
6826
+ if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
6827
+ } else {
6828
+ if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) continue;
6829
+ for (const table of physicalTables) appIds.add(table.appId);
6830
+ }
6831
+ }
6832
+ if (appIds.size === 0) return void 0;
6833
+ const fieldInfosByApp = new Map(
6834
+ await Promise.all([...appIds].map(async (appId) => {
6835
+ const infos = await getFieldsCached(appId, client, cacheContext);
6836
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
6837
+ }))
6838
+ );
6839
+ const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
6840
+ return (ref) => {
6841
+ let info;
6842
+ if (ref.tableAlias !== null) {
6843
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
6844
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
6845
+ } else {
6846
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
6847
+ if (!table || table.cteName !== null) return void 0;
6848
+ info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
6849
+ }
6850
+ } else if (stmt.joins.length === 0) {
6851
+ if (stmt.from.cteName !== null) return void 0;
6852
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
6853
+ } else {
6854
+ if (tables.some((table) => table.cteName !== null)) return void 0;
6855
+ const matches = physicalTables.map((table) => fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field))).filter((candidate) => candidate !== void 0);
6856
+ if (matches.length !== 1) return void 0;
6857
+ info = matches[0];
6858
+ }
6859
+ return info ? aggregateSortKind(info) : void 0;
6860
+ };
6861
+ }
6725
6862
  function fieldCodeForTypeLookup(table, field) {
6726
6863
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
6727
6864
  return field;
@@ -6767,9 +6904,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
6767
6904
  resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
6768
6905
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
6769
6906
  ]);
6770
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
6907
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
6771
6908
  loadTypedPushdownMeta(stmt, client, cacheContext),
6772
- loadTypedInFieldTypes(stmt, client, cacheContext)
6909
+ loadTypedInFieldTypes(stmt, client, cacheContext),
6910
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
6773
6911
  ]);
6774
6912
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
6775
6913
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -6857,6 +6995,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
6857
6995
  sortKinds,
6858
6996
  fieldTypeResolver: fieldTypeResolvers.row,
6859
6997
  havingFieldTypeResolver: fieldTypeResolvers.having,
6998
+ aggregateSortKindResolver,
6860
6999
  appliedKlikes: pushdownPlan.appliedKlikes
6861
7000
  });
6862
7001
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
@@ -6940,9 +7079,10 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
6940
7079
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
6941
7080
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
6942
7081
  ]);
6943
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
7082
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
6944
7083
  loadTypedPushdownMeta(stmt, client, cacheContext),
6945
- loadTypedInFieldTypes(stmt, client, cacheContext)
7084
+ loadTypedInFieldTypes(stmt, client, cacheContext),
7085
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
6946
7086
  ]);
6947
7087
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
6948
7088
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -7014,6 +7154,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
7014
7154
  sortKinds,
7015
7155
  fieldTypeResolver: fieldTypeResolvers.row,
7016
7156
  havingFieldTypeResolver: fieldTypeResolvers.having,
7157
+ aggregateSortKindResolver,
7017
7158
  appliedKlikes: pushdownPlan.appliedKlikes,
7018
7159
  sourceColumns
7019
7160
  });
@@ -32754,16 +32754,25 @@ var Parser = class {
32754
32754
  // IN リストの値
32755
32755
  parseInValues() {
32756
32756
  const values = [];
32757
+ const invalidValueMessage = "IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u3001\u6570\u5024\u3001\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059";
32757
32758
  do {
32758
32759
  const tok = this.advance();
32759
32760
  if (tok.kind === "STRING" /* STRING */) {
32760
32761
  values.push({ type: "STRING", value: tok.value });
32761
32762
  } else if (tok.kind === "NUMBER" /* NUMBER */) {
32762
32763
  values.push({ type: "NUMBER", value: Number(tok.value) });
32764
+ } else if (tok.kind === "-" /* MINUS */ || tok.kind === "+" /* PLUS */) {
32765
+ const number4 = this.peek();
32766
+ if (number4.kind !== "NUMBER" /* NUMBER */) {
32767
+ throw new ParseError(invalidValueMessage, tok);
32768
+ }
32769
+ this.advance();
32770
+ const sign = tok.kind === "-" /* MINUS */ ? -1 : 1;
32771
+ values.push({ type: "NUMBER", value: sign * Number(number4.value) });
32763
32772
  } else if (tok.kind === "VARIABLE" /* VARIABLE */) {
32764
32773
  values.push({ type: "VARIABLE", name: tok.value.slice(1).toLowerCase() });
32765
32774
  } else {
32766
- throw new ParseError("IN \u30EA\u30B9\u30C8\u306B\u306F\u6587\u5B57\u5217\u3001\u6570\u5024\u3001\u307E\u305F\u306F\u30D0\u30C3\u30C1\u5909\u6570\u304C\u5FC5\u8981\u3067\u3059", tok);
32775
+ throw new ParseError(invalidValueMessage, tok);
32767
32776
  }
32768
32777
  } while (this.consume("," /* COMMA */));
32769
32778
  return values;
@@ -35990,7 +35999,7 @@ function hasAggregateColumns(columns) {
35990
35999
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
35991
36000
  );
35992
36001
  }
35993
- function applyGroupBy(rows, groupByKeys, columns) {
36002
+ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
35994
36003
  const groups = /* @__PURE__ */ new Map();
35995
36004
  for (const row of rows) {
35996
36005
  const key = groupByKeys.map((k) => evalGroupByKey(k, row)).join("\0");
@@ -36014,15 +36023,15 @@ function applyGroupBy(rows, groupByKeys, columns) {
36014
36023
  for (const col of columns) {
36015
36024
  if (col.type === "AGGREGATE") {
36016
36025
  const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
36017
- const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows));
36026
+ const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows, resolveAggSortKind));
36018
36027
  outRow[col.alias ?? syntheticKey] = value;
36019
36028
  if (col.alias) outRow[syntheticKey] = value;
36020
36029
  } else if (col.type === "ARITH_AGG_COL") {
36021
36030
  const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
36022
- outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows));
36031
+ outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows, resolveAggSortKind));
36023
36032
  } else if (col.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(col.expr)) {
36024
36033
  const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
36025
- const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows);
36034
+ const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
36026
36035
  outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
36027
36036
  }
36028
36037
  }
@@ -36035,7 +36044,7 @@ function evalGroupByKey(key, row) {
36035
36044
  if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
36036
36045
  return String(evalArithExpr(key.expr, row));
36037
36046
  }
36038
- function evalAggregate(func, distinct, arg, rows) {
36047
+ function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
36039
36048
  if (arg.type === "WILDCARD") {
36040
36049
  return func === "COUNT" ? rows.length : 0;
36041
36050
  }
@@ -36055,6 +36064,11 @@ function evalAggregate(func, distinct, arg, rows) {
36055
36064
  }
36056
36065
  const eff = distinct ? [...new Set(strValues)] : strValues;
36057
36066
  if (func === "COUNT") return eff.length;
36067
+ const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
36068
+ if (sortKind === "string") {
36069
+ if (eff.length === 0) return "";
36070
+ return func === "MAX" ? maxStringOf(eff) : minStringOf(eff);
36071
+ }
36058
36072
  const nums = eff.map(Number);
36059
36073
  switch (func) {
36060
36074
  case "SUM":
@@ -36068,6 +36082,20 @@ function evalAggregate(func, distinct, arg, rows) {
36068
36082
  return nums.length === 0 ? 0 : minOf(nums);
36069
36083
  }
36070
36084
  }
36085
+ function toAggregateFieldRef(field) {
36086
+ const dot = field.indexOf(".");
36087
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
36088
+ }
36089
+ function maxStringOf(values) {
36090
+ let value = values[0];
36091
+ for (const candidate of values) if (candidate > value) value = candidate;
36092
+ return value;
36093
+ }
36094
+ function minStringOf(values) {
36095
+ let value = values[0];
36096
+ for (const candidate of values) if (candidate < value) value = candidate;
36097
+ return value;
36098
+ }
36071
36099
  function maxOf(nums) {
36072
36100
  let m = nums[0];
36073
36101
  for (const n of nums) if (n > m) m = n;
@@ -36078,11 +36106,11 @@ function minOf(nums) {
36078
36106
  for (const n of nums) if (n < m) m = n;
36079
36107
  return m;
36080
36108
  }
36081
- function evalAggArithExpr(node, rows) {
36109
+ function evalAggArithExpr(node, rows, resolveAggSortKind) {
36082
36110
  if (node.type === "NUMBER") return node.value;
36083
- if (node.type === "AGG_REF") return evalAggregate(node.func, node.distinct, node.arg, rows);
36084
- const l = evalAggArithExpr(node.left, rows);
36085
- const r = evalAggArithExpr(node.right, rows);
36111
+ if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, rows, resolveAggSortKind));
36112
+ const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
36113
+ const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
36086
36114
  switch (node.op) {
36087
36115
  case "+":
36088
36116
  return l + r;
@@ -36427,26 +36455,24 @@ function hasAggregateInStringFuncArg(arg) {
36427
36455
  function hasAggregateInStringFuncExpr2(expr) {
36428
36456
  return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
36429
36457
  }
36430
- function resolveAggInStringFuncArg(arg, rows) {
36458
+ function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
36431
36459
  if (arg.type === "AGG_REF") {
36432
- return {
36433
- type: "NUMBER",
36434
- value: evalAggregate(arg.func, arg.distinct, arg.arg, rows)
36435
- };
36460
+ const value = evalAggregate(arg.func, arg.distinct, arg.arg, rows, resolveAggSortKind);
36461
+ return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
36436
36462
  }
36437
36463
  if (arg.type === "AGG_ARITH") {
36438
- return { type: "NUMBER", value: evalAggArithExpr(arg, rows) };
36464
+ return { type: "NUMBER", value: evalAggArithExpr(arg, rows, resolveAggSortKind) };
36439
36465
  }
36440
36466
  if (arg.type === "STRING_FUNC") {
36441
- return resolveAggInStringFuncExpr(arg, rows);
36467
+ return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
36442
36468
  }
36443
36469
  return arg;
36444
36470
  }
36445
- function resolveAggInStringFuncExpr(expr, rows) {
36471
+ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
36446
36472
  return {
36447
36473
  type: "STRING_FUNC",
36448
36474
  func: expr.func,
36449
- args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows))
36475
+ args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
36450
36476
  };
36451
36477
  }
36452
36478
  function runFullScan(input) {
@@ -36458,6 +36484,7 @@ function runFullScan(input) {
36458
36484
  sortKinds,
36459
36485
  fieldTypeResolver,
36460
36486
  havingFieldTypeResolver,
36487
+ aggregateSortKindResolver,
36461
36488
  appliedKlikes,
36462
36489
  sourceColumns
36463
36490
  } = input;
@@ -36473,7 +36500,7 @@ function runFullScan(input) {
36473
36500
  }
36474
36501
  rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
36475
36502
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
36476
- rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
36503
+ rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
36477
36504
  }
36478
36505
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
36479
36506
  if (stmt.distinct) {
@@ -37622,6 +37649,116 @@ async function loadTypedInFieldTypes(stmt, client, cacheContext) {
37622
37649
  const entries = await Promise.all([...appIds].map(async (appId) => [appId, await getFieldTypeMap(appId, client, cacheContext)]));
37623
37650
  return new Map(entries);
37624
37651
  }
37652
+ function aggregateFieldRef(field) {
37653
+ const dot = field.indexOf(".");
37654
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
37655
+ }
37656
+ function collectAggregateRef(func, arg, out) {
37657
+ if ((func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" && arg.field) {
37658
+ out.push(aggregateFieldRef(arg.field));
37659
+ }
37660
+ }
37661
+ function collectAggregateOperandRefs(node, out) {
37662
+ if (node.type === "AGG_REF") {
37663
+ collectAggregateRef(node.func, node.arg, out);
37664
+ return;
37665
+ }
37666
+ if (node.type === "AGG_ARITH") {
37667
+ collectAggregateOperandRefs(node.left, out);
37668
+ collectAggregateOperandRefs(node.right, out);
37669
+ }
37670
+ }
37671
+ function collectStringFuncAggregateRefs(expr, out) {
37672
+ for (const arg of expr.args) {
37673
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
37674
+ collectAggregateOperandRefs(arg, out);
37675
+ } else if (arg.type === "STRING_FUNC") {
37676
+ collectStringFuncAggregateRefs(arg, out);
37677
+ }
37678
+ }
37679
+ }
37680
+ function collectSelectAggregateSortRefs(columns) {
37681
+ const refs = [];
37682
+ for (const column of columns) {
37683
+ if (column.type === "AGGREGATE") {
37684
+ collectAggregateRef(column.func, column.arg, refs);
37685
+ } else if (column.type === "ARITH_AGG_COL") {
37686
+ collectAggregateOperandRefs(column.expr, refs);
37687
+ } else if (column.type === "STRFUNC_COL") {
37688
+ collectStringFuncAggregateRefs(column.expr, refs);
37689
+ }
37690
+ }
37691
+ return refs;
37692
+ }
37693
+ var AGGREGATE_STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
37694
+ "SINGLE_LINE_TEXT",
37695
+ "MULTI_LINE_TEXT",
37696
+ "RICH_TEXT",
37697
+ "LINK",
37698
+ "DROP_DOWN",
37699
+ "RADIO_BUTTON",
37700
+ "STATUS",
37701
+ "DATE",
37702
+ "TIME",
37703
+ "DATETIME",
37704
+ "CREATED_TIME",
37705
+ "UPDATED_TIME"
37706
+ ]);
37707
+ function aggregateSortKind(info) {
37708
+ if (info.sortKind !== void 0) return info.sortKind;
37709
+ if (info.fieldType === "NUMBER" || info.fieldType === "RECORD_NUMBER") return "number";
37710
+ return AGGREGATE_STRING_FIELD_TYPES.has(info.fieldType) ? "string" : void 0;
37711
+ }
37712
+ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
37713
+ const refs = collectSelectAggregateSortRefs(stmt.columns);
37714
+ if (refs.length === 0) return void 0;
37715
+ const appIds = /* @__PURE__ */ new Set();
37716
+ const physicalTables = physicalSelectTables(stmt);
37717
+ for (const ref of refs) {
37718
+ if (ref.tableAlias !== null) {
37719
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
37720
+ appIds.add(stmt.from.appId);
37721
+ continue;
37722
+ }
37723
+ const table = findTableForAlias(stmt, ref.tableAlias);
37724
+ if (table && table.cteName === null) appIds.add(table.appId);
37725
+ } else if (stmt.joins.length === 0) {
37726
+ if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
37727
+ } else {
37728
+ if ([stmt.from, ...stmt.joins.map((join) => join.table)].some((table) => table.cteName !== null)) continue;
37729
+ for (const table of physicalTables) appIds.add(table.appId);
37730
+ }
37731
+ }
37732
+ if (appIds.size === 0) return void 0;
37733
+ const fieldInfosByApp = new Map(
37734
+ await Promise.all([...appIds].map(async (appId) => {
37735
+ const infos = await getFieldsCached(appId, client, cacheContext);
37736
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
37737
+ }))
37738
+ );
37739
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
37740
+ return (ref) => {
37741
+ let info;
37742
+ if (ref.tableAlias !== null) {
37743
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
37744
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
37745
+ } else {
37746
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
37747
+ if (!table || table.cteName !== null) return void 0;
37748
+ info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37749
+ }
37750
+ } else if (stmt.joins.length === 0) {
37751
+ if (stmt.from.cteName !== null) return void 0;
37752
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
37753
+ } else {
37754
+ if (tables.some((table) => table.cteName !== null)) return void 0;
37755
+ const matches = physicalTables.map((table) => fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field))).filter((candidate) => candidate !== void 0);
37756
+ if (matches.length !== 1) return void 0;
37757
+ info = matches[0];
37758
+ }
37759
+ return info ? aggregateSortKind(info) : void 0;
37760
+ };
37761
+ }
37625
37762
  function fieldCodeForTypeLookup(table, field) {
37626
37763
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
37627
37764
  return field;
@@ -37667,9 +37804,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
37667
37804
  resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
37668
37805
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
37669
37806
  ]);
37670
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
37807
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
37671
37808
  loadTypedPushdownMeta(stmt, client, cacheContext),
37672
- loadTypedInFieldTypes(stmt, client, cacheContext)
37809
+ loadTypedInFieldTypes(stmt, client, cacheContext),
37810
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
37673
37811
  ]);
37674
37812
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
37675
37813
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -37757,6 +37895,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
37757
37895
  sortKinds,
37758
37896
  fieldTypeResolver: fieldTypeResolvers.row,
37759
37897
  havingFieldTypeResolver: fieldTypeResolvers.having,
37898
+ aggregateSortKindResolver,
37760
37899
  appliedKlikes: pushdownPlan.appliedKlikes
37761
37900
  });
37762
37901
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
@@ -37840,9 +37979,10 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37840
37979
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
37841
37980
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
37842
37981
  ]);
37843
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
37982
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
37844
37983
  loadTypedPushdownMeta(stmt, client, cacheContext),
37845
- loadTypedInFieldTypes(stmt, client, cacheContext)
37984
+ loadTypedInFieldTypes(stmt, client, cacheContext),
37985
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
37846
37986
  ]);
37847
37987
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
37848
37988
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -37914,6 +38054,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37914
38054
  sortKinds,
37915
38055
  fieldTypeResolver: fieldTypeResolvers.row,
37916
38056
  havingFieldTypeResolver: fieldTypeResolvers.having,
38057
+ aggregateSortKindResolver,
37917
38058
  appliedKlikes: pushdownPlan.appliedKlikes,
37918
38059
  sourceColumns
37919
38060
  });
@@ -42078,7 +42219,7 @@ Options:
42078
42219
  -h, --help Show help
42079
42220
  `);
42080
42221
  }
42081
- var SERVER_VERSION = true ? "2.13.0" : "0.0.0-dev";
42222
+ var SERVER_VERSION = true ? "2.14.1" : "0.0.0-dev";
42082
42223
  function createServer(args) {
42083
42224
  const server = new McpServer({
42084
42225
  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.13.0",
3
+ "version": "2.14.1",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"