@rex0220/kintone-sql-tools 2.13.0 → 2.14.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
@@ -5090,7 +5090,7 @@ function hasAggregateColumns(columns) {
5090
5090
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
5091
5091
  );
5092
5092
  }
5093
- function applyGroupBy(rows, groupByKeys, columns) {
5093
+ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
5094
5094
  const groups = /* @__PURE__ */ new Map();
5095
5095
  for (const row of rows) {
5096
5096
  const key = groupByKeys.map((k) => evalGroupByKey(k, row)).join("\0");
@@ -5114,15 +5114,15 @@ function applyGroupBy(rows, groupByKeys, columns) {
5114
5114
  for (const col of columns) {
5115
5115
  if (col.type === "AGGREGATE") {
5116
5116
  const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
5117
- const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows));
5117
+ const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows, resolveAggSortKind));
5118
5118
  outRow[col.alias ?? syntheticKey] = value;
5119
5119
  if (col.alias) outRow[syntheticKey] = value;
5120
5120
  } else if (col.type === "ARITH_AGG_COL") {
5121
5121
  const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
5122
- outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows));
5122
+ outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows, resolveAggSortKind));
5123
5123
  } else if (col.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(col.expr)) {
5124
5124
  const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
5125
- const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows);
5125
+ const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
5126
5126
  outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
5127
5127
  }
5128
5128
  }
@@ -5135,7 +5135,7 @@ function evalGroupByKey(key, row) {
5135
5135
  if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
5136
5136
  return String(evalArithExpr(key.expr, row));
5137
5137
  }
5138
- function evalAggregate(func, distinct, arg, rows) {
5138
+ function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
5139
5139
  if (arg.type === "WILDCARD") {
5140
5140
  return func === "COUNT" ? rows.length : 0;
5141
5141
  }
@@ -5155,6 +5155,11 @@ function evalAggregate(func, distinct, arg, rows) {
5155
5155
  }
5156
5156
  const eff = distinct ? [...new Set(strValues)] : strValues;
5157
5157
  if (func === "COUNT") return eff.length;
5158
+ const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
5159
+ if (sortKind === "string") {
5160
+ if (eff.length === 0) return "";
5161
+ return func === "MAX" ? maxStringOf(eff) : minStringOf(eff);
5162
+ }
5158
5163
  const nums = eff.map(Number);
5159
5164
  switch (func) {
5160
5165
  case "SUM":
@@ -5168,6 +5173,20 @@ function evalAggregate(func, distinct, arg, rows) {
5168
5173
  return nums.length === 0 ? 0 : minOf(nums);
5169
5174
  }
5170
5175
  }
5176
+ function toAggregateFieldRef(field) {
5177
+ const dot = field.indexOf(".");
5178
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
5179
+ }
5180
+ function maxStringOf(values) {
5181
+ let value = values[0];
5182
+ for (const candidate of values) if (candidate > value) value = candidate;
5183
+ return value;
5184
+ }
5185
+ function minStringOf(values) {
5186
+ let value = values[0];
5187
+ for (const candidate of values) if (candidate < value) value = candidate;
5188
+ return value;
5189
+ }
5171
5190
  function maxOf(nums) {
5172
5191
  let m = nums[0];
5173
5192
  for (const n of nums) if (n > m) m = n;
@@ -5178,11 +5197,11 @@ function minOf(nums) {
5178
5197
  for (const n of nums) if (n < m) m = n;
5179
5198
  return m;
5180
5199
  }
5181
- function evalAggArithExpr(node, rows) {
5200
+ function evalAggArithExpr(node, rows, resolveAggSortKind) {
5182
5201
  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);
5202
+ if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, rows, resolveAggSortKind));
5203
+ const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
5204
+ const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
5186
5205
  switch (node.op) {
5187
5206
  case "+":
5188
5207
  return l + r;
@@ -5527,26 +5546,24 @@ function hasAggregateInStringFuncArg(arg) {
5527
5546
  function hasAggregateInStringFuncExpr2(expr) {
5528
5547
  return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
5529
5548
  }
5530
- function resolveAggInStringFuncArg(arg, rows) {
5549
+ function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
5531
5550
  if (arg.type === "AGG_REF") {
5532
- return {
5533
- type: "NUMBER",
5534
- value: evalAggregate(arg.func, arg.distinct, arg.arg, rows)
5535
- };
5551
+ const value = evalAggregate(arg.func, arg.distinct, arg.arg, rows, resolveAggSortKind);
5552
+ return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
5536
5553
  }
5537
5554
  if (arg.type === "AGG_ARITH") {
5538
- return { type: "NUMBER", value: evalAggArithExpr(arg, rows) };
5555
+ return { type: "NUMBER", value: evalAggArithExpr(arg, rows, resolveAggSortKind) };
5539
5556
  }
5540
5557
  if (arg.type === "STRING_FUNC") {
5541
- return resolveAggInStringFuncExpr(arg, rows);
5558
+ return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
5542
5559
  }
5543
5560
  return arg;
5544
5561
  }
5545
- function resolveAggInStringFuncExpr(expr, rows) {
5562
+ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
5546
5563
  return {
5547
5564
  type: "STRING_FUNC",
5548
5565
  func: expr.func,
5549
- args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows))
5566
+ args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
5550
5567
  };
5551
5568
  }
5552
5569
  function runFullScan(input) {
@@ -5558,6 +5575,7 @@ function runFullScan(input) {
5558
5575
  sortKinds,
5559
5576
  fieldTypeResolver,
5560
5577
  havingFieldTypeResolver,
5578
+ aggregateSortKindResolver,
5561
5579
  appliedKlikes,
5562
5580
  sourceColumns
5563
5581
  } = input;
@@ -5573,7 +5591,7 @@ function runFullScan(input) {
5573
5591
  }
5574
5592
  rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
5575
5593
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
5576
- rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
5594
+ rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
5577
5595
  }
5578
5596
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
5579
5597
  if (stmt.distinct) {
@@ -6722,6 +6740,116 @@ async function loadTypedInFieldTypes(stmt, client, cacheContext) {
6722
6740
  const entries = await Promise.all([...appIds].map(async (appId) => [appId, await getFieldTypeMap(appId, client, cacheContext)]));
6723
6741
  return new Map(entries);
6724
6742
  }
6743
+ function aggregateFieldRef(field) {
6744
+ const dot = field.indexOf(".");
6745
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
6746
+ }
6747
+ function collectAggregateRef(func, arg, out) {
6748
+ if ((func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" && arg.field) {
6749
+ out.push(aggregateFieldRef(arg.field));
6750
+ }
6751
+ }
6752
+ function collectAggregateOperandRefs(node, out) {
6753
+ if (node.type === "AGG_REF") {
6754
+ collectAggregateRef(node.func, node.arg, out);
6755
+ return;
6756
+ }
6757
+ if (node.type === "AGG_ARITH") {
6758
+ collectAggregateOperandRefs(node.left, out);
6759
+ collectAggregateOperandRefs(node.right, out);
6760
+ }
6761
+ }
6762
+ function collectStringFuncAggregateRefs(expr, out) {
6763
+ for (const arg of expr.args) {
6764
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
6765
+ collectAggregateOperandRefs(arg, out);
6766
+ } else if (arg.type === "STRING_FUNC") {
6767
+ collectStringFuncAggregateRefs(arg, out);
6768
+ }
6769
+ }
6770
+ }
6771
+ function collectSelectAggregateSortRefs(columns) {
6772
+ const refs = [];
6773
+ for (const column of columns) {
6774
+ if (column.type === "AGGREGATE") {
6775
+ collectAggregateRef(column.func, column.arg, refs);
6776
+ } else if (column.type === "ARITH_AGG_COL") {
6777
+ collectAggregateOperandRefs(column.expr, refs);
6778
+ } else if (column.type === "STRFUNC_COL") {
6779
+ collectStringFuncAggregateRefs(column.expr, refs);
6780
+ }
6781
+ }
6782
+ return refs;
6783
+ }
6784
+ var AGGREGATE_STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
6785
+ "SINGLE_LINE_TEXT",
6786
+ "MULTI_LINE_TEXT",
6787
+ "RICH_TEXT",
6788
+ "LINK",
6789
+ "DROP_DOWN",
6790
+ "RADIO_BUTTON",
6791
+ "STATUS",
6792
+ "DATE",
6793
+ "TIME",
6794
+ "DATETIME",
6795
+ "CREATED_TIME",
6796
+ "UPDATED_TIME"
6797
+ ]);
6798
+ function aggregateSortKind(info) {
6799
+ if (info.sortKind !== void 0) return info.sortKind;
6800
+ if (info.fieldType === "NUMBER" || info.fieldType === "RECORD_NUMBER") return "number";
6801
+ return AGGREGATE_STRING_FIELD_TYPES.has(info.fieldType) ? "string" : void 0;
6802
+ }
6803
+ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
6804
+ const refs = collectSelectAggregateSortRefs(stmt.columns);
6805
+ if (refs.length === 0) return void 0;
6806
+ const appIds = /* @__PURE__ */ new Set();
6807
+ const physicalTables = physicalSelectTables(stmt);
6808
+ for (const ref of refs) {
6809
+ if (ref.tableAlias !== null) {
6810
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
6811
+ appIds.add(stmt.from.appId);
6812
+ continue;
6813
+ }
6814
+ const table = findTableForAlias(stmt, ref.tableAlias);
6815
+ if (table && table.cteName === null) appIds.add(table.appId);
6816
+ } else if (stmt.joins.length === 0) {
6817
+ if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
6818
+ } else {
6819
+ if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) continue;
6820
+ for (const table of physicalTables) appIds.add(table.appId);
6821
+ }
6822
+ }
6823
+ if (appIds.size === 0) return void 0;
6824
+ const fieldInfosByApp = new Map(
6825
+ await Promise.all([...appIds].map(async (appId) => {
6826
+ const infos = await getFieldsCached(appId, client, cacheContext);
6827
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
6828
+ }))
6829
+ );
6830
+ const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
6831
+ return (ref) => {
6832
+ let info;
6833
+ if (ref.tableAlias !== null) {
6834
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
6835
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
6836
+ } else {
6837
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
6838
+ if (!table || table.cteName !== null) return void 0;
6839
+ info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
6840
+ }
6841
+ } else if (stmt.joins.length === 0) {
6842
+ if (stmt.from.cteName !== null) return void 0;
6843
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
6844
+ } else {
6845
+ if (tables.some((table) => table.cteName !== null)) return void 0;
6846
+ const matches = physicalTables.map((table) => fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field))).filter((candidate) => candidate !== void 0);
6847
+ if (matches.length !== 1) return void 0;
6848
+ info = matches[0];
6849
+ }
6850
+ return info ? aggregateSortKind(info) : void 0;
6851
+ };
6852
+ }
6725
6853
  function fieldCodeForTypeLookup(table, field) {
6726
6854
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
6727
6855
  return field;
@@ -6767,9 +6895,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
6767
6895
  resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
6768
6896
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
6769
6897
  ]);
6770
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
6898
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
6771
6899
  loadTypedPushdownMeta(stmt, client, cacheContext),
6772
- loadTypedInFieldTypes(stmt, client, cacheContext)
6900
+ loadTypedInFieldTypes(stmt, client, cacheContext),
6901
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
6773
6902
  ]);
6774
6903
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
6775
6904
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -6857,6 +6986,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
6857
6986
  sortKinds,
6858
6987
  fieldTypeResolver: fieldTypeResolvers.row,
6859
6988
  havingFieldTypeResolver: fieldTypeResolvers.having,
6989
+ aggregateSortKindResolver,
6860
6990
  appliedKlikes: pushdownPlan.appliedKlikes
6861
6991
  });
6862
6992
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
@@ -6940,9 +7070,10 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
6940
7070
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
6941
7071
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
6942
7072
  ]);
6943
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
7073
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
6944
7074
  loadTypedPushdownMeta(stmt, client, cacheContext),
6945
- loadTypedInFieldTypes(stmt, client, cacheContext)
7075
+ loadTypedInFieldTypes(stmt, client, cacheContext),
7076
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
6946
7077
  ]);
6947
7078
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
6948
7079
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -7014,6 +7145,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
7014
7145
  sortKinds,
7015
7146
  fieldTypeResolver: fieldTypeResolvers.row,
7016
7147
  havingFieldTypeResolver: fieldTypeResolvers.having,
7148
+ aggregateSortKindResolver,
7017
7149
  appliedKlikes: pushdownPlan.appliedKlikes,
7018
7150
  sourceColumns
7019
7151
  });
@@ -35990,7 +35990,7 @@ function hasAggregateColumns(columns) {
35990
35990
  (c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL" || c.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(c.expr)
35991
35991
  );
35992
35992
  }
35993
- function applyGroupBy(rows, groupByKeys, columns) {
35993
+ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
35994
35994
  const groups = /* @__PURE__ */ new Map();
35995
35995
  for (const row of rows) {
35996
35996
  const key = groupByKeys.map((k) => evalGroupByKey(k, row)).join("\0");
@@ -36014,15 +36014,15 @@ function applyGroupBy(rows, groupByKeys, columns) {
36014
36014
  for (const col of columns) {
36015
36015
  if (col.type === "AGGREGATE") {
36016
36016
  const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
36017
- const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows));
36017
+ const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows, resolveAggSortKind));
36018
36018
  outRow[col.alias ?? syntheticKey] = value;
36019
36019
  if (col.alias) outRow[syntheticKey] = value;
36020
36020
  } else if (col.type === "ARITH_AGG_COL") {
36021
36021
  const outputKey = col.alias ?? aggArithDefaultKey(col.expr);
36022
- outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows));
36022
+ outRow[outputKey] = String(evalAggArithExpr(col.expr, groupRows, resolveAggSortKind));
36023
36023
  } else if (col.type === "STRFUNC_COL" && hasAggregateInStringFuncExpr2(col.expr)) {
36024
36024
  const outputKey = col.alias ?? stringFuncDefaultKey(col.expr);
36025
- const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows);
36025
+ const resolvedExpr = resolveAggInStringFuncExpr(col.expr, groupRows, resolveAggSortKind);
36026
36026
  outRow[outputKey] = evalStringFunc(resolvedExpr, outRow);
36027
36027
  }
36028
36028
  }
@@ -36035,7 +36035,7 @@ function evalGroupByKey(key, row) {
36035
36035
  if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
36036
36036
  return String(evalArithExpr(key.expr, row));
36037
36037
  }
36038
- function evalAggregate(func, distinct, arg, rows) {
36038
+ function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
36039
36039
  if (arg.type === "WILDCARD") {
36040
36040
  return func === "COUNT" ? rows.length : 0;
36041
36041
  }
@@ -36055,6 +36055,11 @@ function evalAggregate(func, distinct, arg, rows) {
36055
36055
  }
36056
36056
  const eff = distinct ? [...new Set(strValues)] : strValues;
36057
36057
  if (func === "COUNT") return eff.length;
36058
+ const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
36059
+ if (sortKind === "string") {
36060
+ if (eff.length === 0) return "";
36061
+ return func === "MAX" ? maxStringOf(eff) : minStringOf(eff);
36062
+ }
36058
36063
  const nums = eff.map(Number);
36059
36064
  switch (func) {
36060
36065
  case "SUM":
@@ -36068,6 +36073,20 @@ function evalAggregate(func, distinct, arg, rows) {
36068
36073
  return nums.length === 0 ? 0 : minOf(nums);
36069
36074
  }
36070
36075
  }
36076
+ function toAggregateFieldRef(field) {
36077
+ const dot = field.indexOf(".");
36078
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
36079
+ }
36080
+ function maxStringOf(values) {
36081
+ let value = values[0];
36082
+ for (const candidate of values) if (candidate > value) value = candidate;
36083
+ return value;
36084
+ }
36085
+ function minStringOf(values) {
36086
+ let value = values[0];
36087
+ for (const candidate of values) if (candidate < value) value = candidate;
36088
+ return value;
36089
+ }
36071
36090
  function maxOf(nums) {
36072
36091
  let m = nums[0];
36073
36092
  for (const n of nums) if (n > m) m = n;
@@ -36078,11 +36097,11 @@ function minOf(nums) {
36078
36097
  for (const n of nums) if (n < m) m = n;
36079
36098
  return m;
36080
36099
  }
36081
- function evalAggArithExpr(node, rows) {
36100
+ function evalAggArithExpr(node, rows, resolveAggSortKind) {
36082
36101
  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);
36102
+ if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, rows, resolveAggSortKind));
36103
+ const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
36104
+ const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
36086
36105
  switch (node.op) {
36087
36106
  case "+":
36088
36107
  return l + r;
@@ -36427,26 +36446,24 @@ function hasAggregateInStringFuncArg(arg) {
36427
36446
  function hasAggregateInStringFuncExpr2(expr) {
36428
36447
  return expr.args.some((arg) => hasAggregateInStringFuncArg(arg));
36429
36448
  }
36430
- function resolveAggInStringFuncArg(arg, rows) {
36449
+ function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
36431
36450
  if (arg.type === "AGG_REF") {
36432
- return {
36433
- type: "NUMBER",
36434
- value: evalAggregate(arg.func, arg.distinct, arg.arg, rows)
36435
- };
36451
+ const value = evalAggregate(arg.func, arg.distinct, arg.arg, rows, resolveAggSortKind);
36452
+ return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
36436
36453
  }
36437
36454
  if (arg.type === "AGG_ARITH") {
36438
- return { type: "NUMBER", value: evalAggArithExpr(arg, rows) };
36455
+ return { type: "NUMBER", value: evalAggArithExpr(arg, rows, resolveAggSortKind) };
36439
36456
  }
36440
36457
  if (arg.type === "STRING_FUNC") {
36441
- return resolveAggInStringFuncExpr(arg, rows);
36458
+ return resolveAggInStringFuncExpr(arg, rows, resolveAggSortKind);
36442
36459
  }
36443
36460
  return arg;
36444
36461
  }
36445
- function resolveAggInStringFuncExpr(expr, rows) {
36462
+ function resolveAggInStringFuncExpr(expr, rows, resolveAggSortKind) {
36446
36463
  return {
36447
36464
  type: "STRING_FUNC",
36448
36465
  func: expr.func,
36449
- args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows))
36466
+ args: expr.args.map((arg) => resolveAggInStringFuncArg(arg, rows, resolveAggSortKind))
36450
36467
  };
36451
36468
  }
36452
36469
  function runFullScan(input) {
@@ -36458,6 +36475,7 @@ function runFullScan(input) {
36458
36475
  sortKinds,
36459
36476
  fieldTypeResolver,
36460
36477
  havingFieldTypeResolver,
36478
+ aggregateSortKindResolver,
36461
36479
  appliedKlikes,
36462
36480
  sourceColumns
36463
36481
  } = input;
@@ -36473,7 +36491,7 @@ function runFullScan(input) {
36473
36491
  }
36474
36492
  rows = applyFilter(rows, stmt.where, fieldTypeResolver, appliedKlikes);
36475
36493
  if (stmt.groupBy.length > 0 || hasAggregateColumns(stmt.columns)) {
36476
- rows = applyGroupBy(rows, stmt.groupBy, stmt.columns);
36494
+ rows = applyGroupBy(rows, stmt.groupBy, stmt.columns, aggregateSortKindResolver);
36477
36495
  }
36478
36496
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver);
36479
36497
  if (stmt.distinct) {
@@ -37622,6 +37640,116 @@ async function loadTypedInFieldTypes(stmt, client, cacheContext) {
37622
37640
  const entries = await Promise.all([...appIds].map(async (appId) => [appId, await getFieldTypeMap(appId, client, cacheContext)]));
37623
37641
  return new Map(entries);
37624
37642
  }
37643
+ function aggregateFieldRef(field) {
37644
+ const dot = field.indexOf(".");
37645
+ return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
37646
+ }
37647
+ function collectAggregateRef(func, arg, out) {
37648
+ if ((func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" && arg.field) {
37649
+ out.push(aggregateFieldRef(arg.field));
37650
+ }
37651
+ }
37652
+ function collectAggregateOperandRefs(node, out) {
37653
+ if (node.type === "AGG_REF") {
37654
+ collectAggregateRef(node.func, node.arg, out);
37655
+ return;
37656
+ }
37657
+ if (node.type === "AGG_ARITH") {
37658
+ collectAggregateOperandRefs(node.left, out);
37659
+ collectAggregateOperandRefs(node.right, out);
37660
+ }
37661
+ }
37662
+ function collectStringFuncAggregateRefs(expr, out) {
37663
+ for (const arg of expr.args) {
37664
+ if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") {
37665
+ collectAggregateOperandRefs(arg, out);
37666
+ } else if (arg.type === "STRING_FUNC") {
37667
+ collectStringFuncAggregateRefs(arg, out);
37668
+ }
37669
+ }
37670
+ }
37671
+ function collectSelectAggregateSortRefs(columns) {
37672
+ const refs = [];
37673
+ for (const column of columns) {
37674
+ if (column.type === "AGGREGATE") {
37675
+ collectAggregateRef(column.func, column.arg, refs);
37676
+ } else if (column.type === "ARITH_AGG_COL") {
37677
+ collectAggregateOperandRefs(column.expr, refs);
37678
+ } else if (column.type === "STRFUNC_COL") {
37679
+ collectStringFuncAggregateRefs(column.expr, refs);
37680
+ }
37681
+ }
37682
+ return refs;
37683
+ }
37684
+ var AGGREGATE_STRING_FIELD_TYPES = /* @__PURE__ */ new Set([
37685
+ "SINGLE_LINE_TEXT",
37686
+ "MULTI_LINE_TEXT",
37687
+ "RICH_TEXT",
37688
+ "LINK",
37689
+ "DROP_DOWN",
37690
+ "RADIO_BUTTON",
37691
+ "STATUS",
37692
+ "DATE",
37693
+ "TIME",
37694
+ "DATETIME",
37695
+ "CREATED_TIME",
37696
+ "UPDATED_TIME"
37697
+ ]);
37698
+ function aggregateSortKind(info) {
37699
+ if (info.sortKind !== void 0) return info.sortKind;
37700
+ if (info.fieldType === "NUMBER" || info.fieldType === "RECORD_NUMBER") return "number";
37701
+ return AGGREGATE_STRING_FIELD_TYPES.has(info.fieldType) ? "string" : void 0;
37702
+ }
37703
+ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
37704
+ const refs = collectSelectAggregateSortRefs(stmt.columns);
37705
+ if (refs.length === 0) return void 0;
37706
+ const appIds = /* @__PURE__ */ new Set();
37707
+ const physicalTables = physicalSelectTables(stmt);
37708
+ for (const ref of refs) {
37709
+ if (ref.tableAlias !== null) {
37710
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
37711
+ appIds.add(stmt.from.appId);
37712
+ continue;
37713
+ }
37714
+ const table = findTableForAlias(stmt, ref.tableAlias);
37715
+ if (table && table.cteName === null) appIds.add(table.appId);
37716
+ } else if (stmt.joins.length === 0) {
37717
+ if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
37718
+ } else {
37719
+ if ([stmt.from, ...stmt.joins.map((join) => join.table)].some((table) => table.cteName !== null)) continue;
37720
+ for (const table of physicalTables) appIds.add(table.appId);
37721
+ }
37722
+ }
37723
+ if (appIds.size === 0) return void 0;
37724
+ const fieldInfosByApp = new Map(
37725
+ await Promise.all([...appIds].map(async (appId) => {
37726
+ const infos = await getFieldsCached(appId, client, cacheContext);
37727
+ return [appId, new Map(infos.map((info) => [info.code, info]))];
37728
+ }))
37729
+ );
37730
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
37731
+ return (ref) => {
37732
+ let info;
37733
+ if (ref.tableAlias !== null) {
37734
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
37735
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
37736
+ } else {
37737
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
37738
+ if (!table || table.cteName !== null) return void 0;
37739
+ info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37740
+ }
37741
+ } else if (stmt.joins.length === 0) {
37742
+ if (stmt.from.cteName !== null) return void 0;
37743
+ info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
37744
+ } else {
37745
+ if (tables.some((table) => table.cteName !== null)) return void 0;
37746
+ const matches = physicalTables.map((table) => fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field))).filter((candidate) => candidate !== void 0);
37747
+ if (matches.length !== 1) return void 0;
37748
+ info = matches[0];
37749
+ }
37750
+ return info ? aggregateSortKind(info) : void 0;
37751
+ };
37752
+ }
37625
37753
  function fieldCodeForTypeLookup(table, field) {
37626
37754
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
37627
37755
  return field;
@@ -37667,9 +37795,10 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
37667
37795
  resolveSubqueries(stmt.where, client, options, cacheContext, cteCache),
37668
37796
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache)
37669
37797
  ]);
37670
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
37798
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
37671
37799
  loadTypedPushdownMeta(stmt, client, cacheContext),
37672
- loadTypedInFieldTypes(stmt, client, cacheContext)
37800
+ loadTypedInFieldTypes(stmt, client, cacheContext),
37801
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
37673
37802
  ]);
37674
37803
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
37675
37804
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -37757,6 +37886,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
37757
37886
  sortKinds,
37758
37887
  fieldTypeResolver: fieldTypeResolvers.row,
37759
37888
  havingFieldTypeResolver: fieldTypeResolvers.having,
37889
+ aggregateSortKindResolver,
37760
37890
  appliedKlikes: pushdownPlan.appliedKlikes
37761
37891
  });
37762
37892
  return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
@@ -37840,9 +37970,10 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37840
37970
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
37841
37971
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
37842
37972
  ]);
37843
- const [pushdownMeta, typedInFieldTypes] = await Promise.all([
37973
+ const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
37844
37974
  loadTypedPushdownMeta(stmt, client, cacheContext),
37845
- loadTypedInFieldTypes(stmt, client, cacheContext)
37975
+ loadTypedInFieldTypes(stmt, client, cacheContext),
37976
+ loadAggregateSortKindResolver(stmt, client, cacheContext)
37846
37977
  ]);
37847
37978
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
37848
37979
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -37914,6 +38045,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37914
38045
  sortKinds,
37915
38046
  fieldTypeResolver: fieldTypeResolvers.row,
37916
38047
  havingFieldTypeResolver: fieldTypeResolvers.having,
38048
+ aggregateSortKindResolver,
37917
38049
  appliedKlikes: pushdownPlan.appliedKlikes,
37918
38050
  sourceColumns
37919
38051
  });
@@ -42078,7 +42210,7 @@ Options:
42078
42210
  -h, --help Show help
42079
42211
  `);
42080
42212
  }
42081
- var SERVER_VERSION = true ? "2.13.0" : "0.0.0-dev";
42213
+ var SERVER_VERSION = true ? "2.14.0" : "0.0.0-dev";
42082
42214
  function createServer(args) {
42083
42215
  const server = new McpServer({
42084
42216
  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.0",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"