@rex0220/kintone-sql-tools 2.14.1 → 2.15.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
@@ -87,6 +87,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
87
87
  ["AVG", "AVG" /* AVG */],
88
88
  ["MAX", "MAX" /* MAX */],
89
89
  ["MIN", "MIN" /* MIN */],
90
+ ["GROUP_CONCAT", "GROUP_CONCAT" /* GROUP_CONCAT */],
90
91
  ["ASSERT", "ASSERT" /* ASSERT */],
91
92
  ["AND", "AND" /* AND */],
92
93
  ["OR", "OR" /* OR */],
@@ -1079,6 +1080,7 @@ var Parser = class {
1079
1080
  func: ref.func,
1080
1081
  distinct: ref.distinct,
1081
1082
  arg: ref.arg,
1083
+ ...ref.separator !== void 0 ? { separator: ref.separator } : {},
1082
1084
  alias: alias2
1083
1085
  };
1084
1086
  }
@@ -1450,7 +1452,8 @@ var Parser = class {
1450
1452
  ["SUM" /* SUM */]: "SUM",
1451
1453
  ["AVG" /* AVG */]: "AVG",
1452
1454
  ["MAX" /* MAX */]: "MAX",
1453
- ["MIN" /* MIN */]: "MIN"
1455
+ ["MIN" /* MIN */]: "MIN",
1456
+ ["GROUP_CONCAT" /* GROUP_CONCAT */]: "GROUP_CONCAT"
1454
1457
  };
1455
1458
  const kind = this.peek().kind;
1456
1459
  return map[kind] ?? null;
@@ -1462,12 +1465,29 @@ var Parser = class {
1462
1465
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
1463
1466
  let arg;
1464
1467
  if (this.consume("*" /* STAR */)) {
1468
+ if (func === "GROUP_CONCAT") {
1469
+ throw new ParseError("GROUP_CONCAT(*) \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D5\u30A3\u30FC\u30EB\u30C9\u307E\u305F\u306F\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
1470
+ }
1465
1471
  arg = { type: "WILDCARD" };
1466
1472
  } else {
1467
1473
  arg = this.parseArithAddSub();
1468
1474
  }
1475
+ let separator;
1476
+ if (this.isSoftKeyword("SEPARATOR")) {
1477
+ const separatorToken = this.advance();
1478
+ if (func !== "GROUP_CONCAT") {
1479
+ throw new ParseError("SEPARATOR \u306F GROUP_CONCAT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", separatorToken);
1480
+ }
1481
+ separator = this.expect("STRING" /* STRING */, "SEPARATOR \u306E\u5F8C\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059").value;
1482
+ }
1469
1483
  this.expect(")" /* RPAREN */);
1470
- return { type: "AGG_REF", func, distinct, arg };
1484
+ return {
1485
+ type: "AGG_REF",
1486
+ func,
1487
+ distinct,
1488
+ arg,
1489
+ ...separator !== void 0 ? { separator } : {}
1490
+ };
1471
1491
  }
1472
1492
  // ----------------------------------------------------------
1473
1493
  // FROM / JOIN
@@ -1745,10 +1765,20 @@ var Parser = class {
1745
1765
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
1746
1766
  let argStr;
1747
1767
  if (this.consume("*" /* STAR */)) {
1768
+ if (aggFunc === "GROUP_CONCAT") {
1769
+ throw new ParseError("GROUP_CONCAT(*) \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D5\u30A3\u30FC\u30EB\u30C9\u307E\u305F\u306F\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
1770
+ }
1748
1771
  argStr = "*";
1749
1772
  } else {
1750
1773
  argStr = this.parseIdentifier();
1751
1774
  }
1775
+ if (this.isSoftKeyword("SEPARATOR")) {
1776
+ const separatorToken = this.advance();
1777
+ if (aggFunc !== "GROUP_CONCAT") {
1778
+ throw new ParseError("SEPARATOR \u306F GROUP_CONCAT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", separatorToken);
1779
+ }
1780
+ this.expect("STRING" /* STRING */, "SEPARATOR \u306E\u5F8C\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
1781
+ }
1752
1782
  this.expect(")" /* RPAREN */);
1753
1783
  const syntheticName = distinct ? `${aggFunc}(DISTINCT ${argStr})` : `${aggFunc}(${argStr})`;
1754
1784
  return { type: "FIELD", tableAlias: null, field: syntheticName };
@@ -3357,7 +3387,7 @@ function stringFuncLabel(expr) {
3357
3387
  return `${expr.func}(${args.join(",")})`;
3358
3388
  }
3359
3389
  function isAggregateSyntheticName(name) {
3360
- return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
3390
+ return /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT)\(/i.test(name);
3361
3391
  }
3362
3392
 
3363
3393
  // src/core/cteInlining.ts
@@ -5123,7 +5153,7 @@ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
5123
5153
  for (const col of columns) {
5124
5154
  if (col.type === "AGGREGATE") {
5125
5155
  const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
5126
- const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows, resolveAggSortKind));
5156
+ const value = String(evalAggregate(col.func, col.distinct, col.arg, col.separator, groupRows, resolveAggSortKind));
5127
5157
  outRow[col.alias ?? syntheticKey] = value;
5128
5158
  if (col.alias) outRow[syntheticKey] = value;
5129
5159
  } else if (col.type === "ARITH_AGG_COL") {
@@ -5144,7 +5174,7 @@ function evalGroupByKey(key, row) {
5144
5174
  if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
5145
5175
  return String(evalArithExpr(key.expr, row));
5146
5176
  }
5147
- function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
5177
+ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind) {
5148
5178
  if (arg.type === "WILDCARD") {
5149
5179
  return func === "COUNT" ? rows.length : 0;
5150
5180
  }
@@ -5164,6 +5194,7 @@ function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
5164
5194
  }
5165
5195
  const eff = distinct ? [...new Set(strValues)] : strValues;
5166
5196
  if (func === "COUNT") return eff.length;
5197
+ if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
5167
5198
  const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
5168
5199
  if (sortKind === "string") {
5169
5200
  if (eff.length === 0) return "";
@@ -5208,7 +5239,7 @@ function minOf(nums) {
5208
5239
  }
5209
5240
  function evalAggArithExpr(node, rows, resolveAggSortKind) {
5210
5241
  if (node.type === "NUMBER") return node.value;
5211
- if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, rows, resolveAggSortKind));
5242
+ if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
5212
5243
  const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
5213
5244
  const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
5214
5245
  switch (node.op) {
@@ -5557,7 +5588,7 @@ function hasAggregateInStringFuncExpr2(expr) {
5557
5588
  }
5558
5589
  function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
5559
5590
  if (arg.type === "AGG_REF") {
5560
- const value = evalAggregate(arg.func, arg.distinct, arg.arg, rows, resolveAggSortKind);
5591
+ const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
5561
5592
  return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
5562
5593
  }
5563
5594
  if (arg.type === "AGG_ARITH") {
@@ -5803,7 +5834,8 @@ function isValidTemporal(value, type) {
5803
5834
  const date = new Date(Date.UTC(year, month - 1, day));
5804
5835
  if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return false;
5805
5836
  if (type === "DATE") return true;
5806
- return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(?:Z|[+-]\d{2}:\d{2})$/.test(value) && isValidTemporal(value.slice(11, value.endsWith("Z") ? -1 : value.length - 6), "TIME");
5837
+ const timePart = value.slice(11, value.endsWith("Z") ? -1 : value.length - 6).replace(/\.\d+$/, "");
5838
+ return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value) && isValidTemporal(timePart, "TIME");
5807
5839
  }
5808
5840
 
5809
5841
  // src/core/dmlValidationCandidates.ts
@@ -5883,6 +5915,8 @@ var SearchAbortedError = class extends Error {
5883
5915
  this.name = "SearchAbortedError";
5884
5916
  }
5885
5917
  };
5918
+ var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
5919
+ var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
5886
5920
  async function execute(sql, client, options = {}) {
5887
5921
  const startedAt = Date.now();
5888
5922
  const stmt = parseSql(sql);
@@ -6028,16 +6062,25 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
6028
6062
  }
6029
6063
  }
6030
6064
  var TEMP_TABLE_MAX_ROWS = 1e4;
6031
- function appendValidationErrors(tempTables, name, columns, rows, maxRows) {
6065
+ function appendValidationErrors(tempTables, name, columns, rows, maxRows, columnMeta) {
6032
6066
  const current = tempTables.get(name);
6033
- if (current && (current.columns.length !== columns.length || current.columns.some((c, i) => c !== columns[i]))) {
6067
+ if (current && (current.columns.length !== columns.length || current.columns.some((c, i) => c !== columns[i]) || !materializedColumnMetaEqual(current.columnMeta, columnMeta))) {
6034
6068
  throw new Error(`ArgumentError: validation error table ${name} has a different schema.`);
6035
6069
  }
6036
6070
  const existingRows = current?.rows ?? [];
6037
6071
  if (existingRows.length + rows.length > maxRows) {
6038
6072
  throw new Error(`ArgumentError: temp table ${name} exceeds max rows (${maxRows}).`);
6039
6073
  }
6040
- tempTables.set(name, { columns: [...columns], rows: [...existingRows, ...rows] });
6074
+ tempTables.set(name, { columns: [...columns], rows: [...existingRows, ...rows], columnMeta });
6075
+ }
6076
+ function materializedColumnMetaEqual(left, right) {
6077
+ if (left === right) return true;
6078
+ if (!left || !right || left.size !== right.size) return false;
6079
+ for (const [column, meta] of left) {
6080
+ const candidate = right.get(column);
6081
+ if (!candidate || candidate.sortKind !== meta.sortKind || candidate.fieldType !== meta.fieldType) return false;
6082
+ }
6083
+ return true;
6041
6084
  }
6042
6085
  var BatchTimeoutError = class extends Error {
6043
6086
  constructor() {
@@ -6199,7 +6242,8 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
6199
6242
  resolvedStmt.validationErrorTable,
6200
6243
  result.columns,
6201
6244
  result.errors,
6202
- options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS
6245
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
6246
+ materializedMetaByValidationResult.get(result) ?? /* @__PURE__ */ new Map()
6203
6247
  );
6204
6248
  }
6205
6249
  return { result };
@@ -6223,7 +6267,11 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
6223
6267
  onLimitReached: "error"
6224
6268
  };
6225
6269
  const result = await runSelectLike(resolvedStmt.query, client, materializeOptions, cacheContext, tempTables);
6226
- tempTables.set(resolvedStmt.name, { rows: result.rows, columns: result.columns });
6270
+ tempTables.set(resolvedStmt.name, {
6271
+ rows: result.rows,
6272
+ columns: result.columns,
6273
+ columnMeta: materializedMetaBySelectResult.get(result)
6274
+ });
6227
6275
  return { tempTable: resolvedStmt.name, rowCount: result.rows.length };
6228
6276
  }
6229
6277
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -6259,9 +6307,9 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
6259
6307
  }
6260
6308
  async function runSelectLike(query, client, options, cacheContext, tempTables) {
6261
6309
  if (query.type === "WITH") {
6262
- return executeWith(query, client, options, cacheContext, tempTables);
6310
+ return executeWith(query, client, options, cacheContext, tempTables, true);
6263
6311
  }
6264
- return executeQueryWithCte(query, client, options, tempTables, cacheContext);
6312
+ return executeQueryWithCte(query, client, options, tempTables, cacheContext, true);
6265
6313
  }
6266
6314
  async function runWithDeadline(work, remainingMs) {
6267
6315
  if (remainingMs === null) return work;
@@ -6474,18 +6522,27 @@ function evalAssertArith(node) {
6474
6522
  }
6475
6523
  throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
6476
6524
  }
6477
- async function executeSelect(stmt, client, options, cacheContext, cteCache) {
6525
+ async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
6526
+ let result;
6478
6527
  if (isNoFromSelect(stmt)) {
6479
- return executeNoFromSelect(stmt);
6528
+ result = executeNoFromSelect(stmt);
6529
+ if (captureColumnMeta) {
6530
+ materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
6531
+ }
6532
+ return result;
6480
6533
  }
6481
6534
  await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
6482
6535
  const mode = resolveSelectMode(stmt);
6483
6536
  await validateSelectFieldCodes(stmt, mode, client, cacheContext);
6484
6537
  if (mode === "SIMPLE") {
6485
- return executeSimpleSelect(stmt, client, options, cacheContext);
6538
+ result = await executeSimpleSelect(stmt, client, options, cacheContext);
6486
6539
  } else {
6487
- return executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
6540
+ result = await executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
6488
6541
  }
6542
+ if (captureColumnMeta) {
6543
+ materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
6544
+ }
6545
+ return result;
6489
6546
  }
6490
6547
  function isNoFromSelect(stmt) {
6491
6548
  return stmt.from.appId === 0 && stmt.from.cteName === NO_FROM_CTE_NAME;
@@ -6809,7 +6866,7 @@ function aggregateSortKind(info) {
6809
6866
  if (info.fieldType === "NUMBER" || info.fieldType === "RECORD_NUMBER") return "number";
6810
6867
  return AGGREGATE_STRING_FIELD_TYPES.has(info.fieldType) ? "string" : void 0;
6811
6868
  }
6812
- async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
6869
+ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materializedTables) {
6813
6870
  const refs = collectSelectAggregateSortRefs(stmt.columns);
6814
6871
  if (refs.length === 0) return void 0;
6815
6872
  const appIds = /* @__PURE__ */ new Set();
@@ -6825,11 +6882,9 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
6825
6882
  } else if (stmt.joins.length === 0) {
6826
6883
  if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
6827
6884
  } else {
6828
- if ([stmt.from, ...stmt.joins.map((join2) => join2.table)].some((table) => table.cteName !== null)) continue;
6829
6885
  for (const table of physicalTables) appIds.add(table.appId);
6830
6886
  }
6831
6887
  }
6832
- if (appIds.size === 0) return void 0;
6833
6888
  const fieldInfosByApp = new Map(
6834
6889
  await Promise.all([...appIds].map(async (appId) => {
6835
6890
  const infos = await getFieldsCached(appId, client, cacheContext);
@@ -6844,17 +6899,28 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
6844
6899
  info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
6845
6900
  } else {
6846
6901
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
6847
- if (!table || table.cteName !== null) return void 0;
6902
+ if (!table) return void 0;
6903
+ if (table.cteName !== null) {
6904
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.sortKind;
6905
+ }
6848
6906
  info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
6849
6907
  }
6850
6908
  } else if (stmt.joins.length === 0) {
6851
- if (stmt.from.cteName !== null) return void 0;
6909
+ if (stmt.from.cteName !== null) {
6910
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.sortKind;
6911
+ }
6852
6912
  info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
6853
6913
  } 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);
6914
+ const matches = tables.flatMap((table) => {
6915
+ if (table.cteName !== null) {
6916
+ const materialized = materializedTables?.get(table.cteName);
6917
+ return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.sortKind] : [];
6918
+ }
6919
+ const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
6920
+ return candidate ? [aggregateSortKind(candidate)] : [];
6921
+ });
6856
6922
  if (matches.length !== 1) return void 0;
6857
- info = matches[0];
6923
+ return matches[0];
6858
6924
  }
6859
6925
  return info ? aggregateSortKind(info) : void 0;
6860
6926
  };
@@ -6863,6 +6929,106 @@ function fieldCodeForTypeLookup(table, field) {
6863
6929
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
6864
6930
  return field;
6865
6931
  }
6932
+ function materializedMetaFromFieldInfo(info) {
6933
+ return { sortKind: aggregateSortKind(info), fieldType: info.fieldType };
6934
+ }
6935
+ function selectNeedsSourceColumnMeta(stmt) {
6936
+ return stmt.columns.some(
6937
+ (column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF"
6938
+ );
6939
+ }
6940
+ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
6941
+ const physicalInfos = /* @__PURE__ */ new Map();
6942
+ if (selectNeedsSourceColumnMeta(stmt)) {
6943
+ await Promise.all(physicalSelectTables(stmt).map(async (table) => {
6944
+ if (physicalInfos.has(table.appId)) return;
6945
+ const infos = await getFieldsCached(table.appId, client, cacheContext);
6946
+ physicalInfos.set(table.appId, new Map(infos.map((info) => [info.code, info])));
6947
+ }));
6948
+ }
6949
+ const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
6950
+ const resolveField2 = (ref) => {
6951
+ if (ref.tableAlias !== null) {
6952
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
6953
+ const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
6954
+ return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
6955
+ }
6956
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
6957
+ if (!table) return void 0;
6958
+ if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
6959
+ const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
6960
+ return info ? materializedMetaFromFieldInfo(info) : void 0;
6961
+ }
6962
+ if (stmt.joins.length === 0) {
6963
+ if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
6964
+ const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
6965
+ return info ? materializedMetaFromFieldInfo(info) : void 0;
6966
+ }
6967
+ const matches = tables.flatMap((table) => {
6968
+ if (table.cteName !== null) {
6969
+ const materialized = materializedTables?.get(table.cteName);
6970
+ if (!materialized?.columns.includes(ref.field)) return [];
6971
+ return [materialized.columnMeta?.get(ref.field)];
6972
+ }
6973
+ const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
6974
+ return info ? [materializedMetaFromFieldInfo(info)] : [];
6975
+ });
6976
+ return matches.length === 1 ? matches[0] : void 0;
6977
+ };
6978
+ const inferred = /* @__PURE__ */ new Map();
6979
+ const hasWildcard = stmt.columns.some((column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD");
6980
+ if (stmt.columns.length === 1 && (stmt.columns[0].type === "WILDCARD" || stmt.columns[0].type === "PARENT_WILDCARD")) {
6981
+ for (const output of outputColumns) {
6982
+ const meta = resolveField2(aggregateFieldRef(output));
6983
+ if (meta) inferred.set(output, meta);
6984
+ }
6985
+ return inferred;
6986
+ }
6987
+ if (hasWildcard) {
6988
+ for (const output of outputColumns) {
6989
+ const meta = resolveField2(aggregateFieldRef(output));
6990
+ if (meta) inferred.set(output, meta);
6991
+ }
6992
+ }
6993
+ const explicitColumns = stmt.columns.filter(
6994
+ (column) => column.type !== "WILDCARD" && column.type !== "PARENT_WILDCARD"
6995
+ );
6996
+ explicitColumns.forEach((column, index) => {
6997
+ const output = hasWildcard ? "alias" in column && column.alias ? column.alias : void 0 : outputColumns[index];
6998
+ if (!output) return;
6999
+ let meta;
7000
+ if (column.type === "FIELD") {
7001
+ meta = resolveField2(aggregateFieldRef(column.field));
7002
+ } else if (column.type === "AGGREGATE") {
7003
+ if (column.func === "GROUP_CONCAT") {
7004
+ meta = { sortKind: "string" };
7005
+ } else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
7006
+ meta = { sortKind: "number" };
7007
+ } else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
7008
+ const source = resolveField2(aggregateFieldRef(column.arg.field));
7009
+ if (source?.sortKind) meta = { sortKind: source.sortKind };
7010
+ }
7011
+ } else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
7012
+ meta = { sortKind: "number" };
7013
+ } else if (column.type === "LITERAL_COL") {
7014
+ meta = { sortKind: "string" };
7015
+ }
7016
+ if (meta) inferred.set(output, meta);
7017
+ });
7018
+ return inferred;
7019
+ }
7020
+ function mergeUnionColumnMeta(left, right) {
7021
+ const leftMeta = materializedMetaBySelectResult.get(left);
7022
+ const rightMeta = materializedMetaBySelectResult.get(right);
7023
+ const merged = /* @__PURE__ */ new Map();
7024
+ left.columns.forEach((column, index) => {
7025
+ const a = leftMeta?.get(column);
7026
+ const rightColumn = right.columns[index];
7027
+ const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
7028
+ if (a && b && a.sortKind === b.sortKind && a.fieldType === b.fieldType) merged.set(column, a);
7029
+ });
7030
+ return merged;
7031
+ }
6866
7032
  function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
6867
7033
  const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
6868
7034
  const physicalTables = tables.filter((table) => table.cteName === null);
@@ -6907,7 +7073,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
6907
7073
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
6908
7074
  loadTypedPushdownMeta(stmt, client, cacheContext),
6909
7075
  loadTypedInFieldTypes(stmt, client, cacheContext),
6910
- loadAggregateSortKindResolver(stmt, client, cacheContext)
7076
+ loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
6911
7077
  ]);
6912
7078
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
6913
7079
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -7027,9 +7193,9 @@ function deduplicateRows(rows, columns) {
7027
7193
  return true;
7028
7194
  });
7029
7195
  }
7030
- async function executeWith(stmt, client, options, cacheContext, seed) {
7196
+ async function executeWith(stmt, client, options, cacheContext, seed, captureColumnMeta = false) {
7031
7197
  if ((seed == null || seed.size === 0) && canInlineSingleCte(stmt)) {
7032
- return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext);
7198
+ return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext, void 0, captureColumnMeta);
7033
7199
  }
7034
7200
  const cteCache = new Map(seed ?? []);
7035
7201
  for (const cte of stmt.ctes) {
@@ -7039,17 +7205,21 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
7039
7205
  } else if (cte.query.type === "DESCRIBE") {
7040
7206
  result = await executeDescribe(cte.query, client, cacheContext);
7041
7207
  } else {
7042
- result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext);
7208
+ result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext, true);
7043
7209
  }
7044
- cteCache.set(cte.name, { rows: result.rows, columns: result.columns });
7210
+ cteCache.set(cte.name, {
7211
+ rows: result.rows,
7212
+ columns: result.columns,
7213
+ columnMeta: materializedMetaBySelectResult.get(result)
7214
+ });
7045
7215
  }
7046
- return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
7216
+ return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext, captureColumnMeta);
7047
7217
  }
7048
- async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
7218
+ async function executeQueryWithCte(query, client, options, cteCache, cacheContext, captureColumnMeta = false) {
7049
7219
  if (query.type === "UNION") {
7050
7220
  const [leftResult, rightResult] = await Promise.all([
7051
- executeQueryWithCte(query.left, client, options, cteCache, cacheContext),
7052
- executeQueryWithCte(query.right, client, options, cteCache, cacheContext)
7221
+ executeQueryWithCte(query.left, client, options, cteCache, cacheContext, captureColumnMeta),
7222
+ executeQueryWithCte(query.right, client, options, cteCache, cacheContext, captureColumnMeta)
7053
7223
  ]);
7054
7224
  const leftCols = leftResult.columns;
7055
7225
  const rightCols = rightResult.columns;
@@ -7062,13 +7232,21 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
7062
7232
  });
7063
7233
  const combined = [...leftResult.rows, ...remapped];
7064
7234
  const rows = query.all ? combined : deduplicateRows(combined, leftCols);
7065
- return { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
7235
+ const result2 = { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
7236
+ if (captureColumnMeta) {
7237
+ materializedMetaBySelectResult.set(result2, mergeUnionColumnMeta(leftResult, rightResult));
7238
+ }
7239
+ return result2;
7066
7240
  }
7067
7241
  const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
7068
7242
  if (!hasCteRef) {
7069
- return executeSelect(query, client, options, cacheContext, cteCache);
7243
+ return executeSelect(query, client, options, cacheContext, cteCache, captureColumnMeta);
7244
+ }
7245
+ const result = await executeFullScanWithCte(query, client, options, cteCache, cacheContext);
7246
+ if (captureColumnMeta) {
7247
+ materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(query, result.columns, client, cacheContext, cteCache));
7070
7248
  }
7071
- return executeFullScanWithCte(query, client, options, cteCache, cacheContext);
7249
+ return result;
7072
7250
  }
7073
7251
  async function executeFullScanWithCte(stmt, client, options, cteCache, cacheContext) {
7074
7252
  const maxRecords = options.maxRecords ?? 1e4;
@@ -7082,7 +7260,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
7082
7260
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
7083
7261
  loadTypedPushdownMeta(stmt, client, cacheContext),
7084
7262
  loadTypedInFieldTypes(stmt, client, cacheContext),
7085
- loadAggregateSortKindResolver(stmt, client, cacheContext)
7263
+ loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
7086
7264
  ]);
7087
7265
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
7088
7266
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -7560,7 +7738,23 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
7560
7738
  errors,
7561
7739
  ...stmt.validationErrorTable ? { errTable: stmt.validationErrorTable } : stmt.onErrorSkip && stmt.errorTable ? { errTable: stmt.errorTable } : {}
7562
7740
  };
7563
- return { result, candidates, invalidRowNumbers };
7741
+ const columnMeta = /* @__PURE__ */ new Map();
7742
+ for (const column of payloadFields) {
7743
+ if (column === "$id") {
7744
+ columnMeta.set(column, { sortKind: "number", fieldType: "RECORD_NUMBER" });
7745
+ continue;
7746
+ }
7747
+ const info = infoByCode.get(column);
7748
+ if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
7749
+ }
7750
+ columnMeta.set("$err_statement", { sortKind: "number" });
7751
+ columnMeta.set("$err_operation", { sortKind: "string" });
7752
+ columnMeta.set("$err_row", { sortKind: "number" });
7753
+ columnMeta.set("$err_field", { sortKind: "string" });
7754
+ columnMeta.set("$err_code", { sortKind: "string" });
7755
+ columnMeta.set("$err_message", { sortKind: "string" });
7756
+ materializedMetaByValidationResult.set(result, columnMeta);
7757
+ return { result, candidates, invalidRowNumbers, columnMeta };
7564
7758
  }
7565
7759
  async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTables, statementNumber) {
7566
7760
  const prepared = await prepareDmlValidation(
@@ -7578,7 +7772,8 @@ async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTable
7578
7772
  errTable,
7579
7773
  prepared.result.columns,
7580
7774
  prepared.result.errors,
7581
- options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS
7775
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
7776
+ prepared.columnMeta
7582
7777
  );
7583
7778
  const rejectLimit = stmt.rejectLimit ?? null;
7584
7779
  if (rejectLimit !== null && prepared.result.invalidRows > rejectLimit) {
@@ -30999,6 +30999,7 @@ var KEYWORDS = /* @__PURE__ */ new Map([
30999
30999
  ["AVG", "AVG" /* AVG */],
31000
31000
  ["MAX", "MAX" /* MAX */],
31001
31001
  ["MIN", "MIN" /* MIN */],
31002
+ ["GROUP_CONCAT", "GROUP_CONCAT" /* GROUP_CONCAT */],
31002
31003
  ["ASSERT", "ASSERT" /* ASSERT */],
31003
31004
  ["AND", "AND" /* AND */],
31004
31005
  ["OR", "OR" /* OR */],
@@ -31991,6 +31992,7 @@ var Parser = class {
31991
31992
  func: ref.func,
31992
31993
  distinct: ref.distinct,
31993
31994
  arg: ref.arg,
31995
+ ...ref.separator !== void 0 ? { separator: ref.separator } : {},
31994
31996
  alias: alias2
31995
31997
  };
31996
31998
  }
@@ -32362,7 +32364,8 @@ var Parser = class {
32362
32364
  ["SUM" /* SUM */]: "SUM",
32363
32365
  ["AVG" /* AVG */]: "AVG",
32364
32366
  ["MAX" /* MAX */]: "MAX",
32365
- ["MIN" /* MIN */]: "MIN"
32367
+ ["MIN" /* MIN */]: "MIN",
32368
+ ["GROUP_CONCAT" /* GROUP_CONCAT */]: "GROUP_CONCAT"
32366
32369
  };
32367
32370
  const kind = this.peek().kind;
32368
32371
  return map2[kind] ?? null;
@@ -32374,12 +32377,29 @@ var Parser = class {
32374
32377
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
32375
32378
  let arg;
32376
32379
  if (this.consume("*" /* STAR */)) {
32380
+ if (func === "GROUP_CONCAT") {
32381
+ throw new ParseError("GROUP_CONCAT(*) \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D5\u30A3\u30FC\u30EB\u30C9\u307E\u305F\u306F\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
32382
+ }
32377
32383
  arg = { type: "WILDCARD" };
32378
32384
  } else {
32379
32385
  arg = this.parseArithAddSub();
32380
32386
  }
32387
+ let separator;
32388
+ if (this.isSoftKeyword("SEPARATOR")) {
32389
+ const separatorToken = this.advance();
32390
+ if (func !== "GROUP_CONCAT") {
32391
+ throw new ParseError("SEPARATOR \u306F GROUP_CONCAT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", separatorToken);
32392
+ }
32393
+ separator = this.expect("STRING" /* STRING */, "SEPARATOR \u306E\u5F8C\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059").value;
32394
+ }
32381
32395
  this.expect(")" /* RPAREN */);
32382
- return { type: "AGG_REF", func, distinct, arg };
32396
+ return {
32397
+ type: "AGG_REF",
32398
+ func,
32399
+ distinct,
32400
+ arg,
32401
+ ...separator !== void 0 ? { separator } : {}
32402
+ };
32383
32403
  }
32384
32404
  // ----------------------------------------------------------
32385
32405
  // FROM / JOIN
@@ -32657,10 +32677,20 @@ var Parser = class {
32657
32677
  const distinct = this.consume("DISTINCT" /* DISTINCT */);
32658
32678
  let argStr;
32659
32679
  if (this.consume("*" /* STAR */)) {
32680
+ if (aggFunc === "GROUP_CONCAT") {
32681
+ throw new ParseError("GROUP_CONCAT(*) \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D5\u30A3\u30FC\u30EB\u30C9\u307E\u305F\u306F\u5F0F\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044", this.prev());
32682
+ }
32660
32683
  argStr = "*";
32661
32684
  } else {
32662
32685
  argStr = this.parseIdentifier();
32663
32686
  }
32687
+ if (this.isSoftKeyword("SEPARATOR")) {
32688
+ const separatorToken = this.advance();
32689
+ if (aggFunc !== "GROUP_CONCAT") {
32690
+ throw new ParseError("SEPARATOR \u306F GROUP_CONCAT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", separatorToken);
32691
+ }
32692
+ this.expect("STRING" /* STRING */, "SEPARATOR \u306E\u5F8C\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
32693
+ }
32664
32694
  this.expect(")" /* RPAREN */);
32665
32695
  const syntheticName = distinct ? `${aggFunc}(DISTINCT ${argStr})` : `${aggFunc}(${argStr})`;
32666
32696
  return { type: "FIELD", tableAlias: null, field: syntheticName };
@@ -34257,7 +34287,7 @@ function stringFuncLabel(expr) {
34257
34287
  return `${expr.func}(${args.join(",")})`;
34258
34288
  }
34259
34289
  function isAggregateSyntheticName(name) {
34260
- return /^(COUNT|SUM|AVG|MAX|MIN)\(/i.test(name);
34290
+ return /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT)\(/i.test(name);
34261
34291
  }
34262
34292
 
34263
34293
  // src/core/cteInlining.ts
@@ -36023,7 +36053,7 @@ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
36023
36053
  for (const col of columns) {
36024
36054
  if (col.type === "AGGREGATE") {
36025
36055
  const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
36026
- const value = String(evalAggregate(col.func, col.distinct, col.arg, groupRows, resolveAggSortKind));
36056
+ const value = String(evalAggregate(col.func, col.distinct, col.arg, col.separator, groupRows, resolveAggSortKind));
36027
36057
  outRow[col.alias ?? syntheticKey] = value;
36028
36058
  if (col.alias) outRow[syntheticKey] = value;
36029
36059
  } else if (col.type === "ARITH_AGG_COL") {
@@ -36044,7 +36074,7 @@ function evalGroupByKey(key, row) {
36044
36074
  if (key.type === "FUNC_KEY") return evalStringFunc(key.expr, row);
36045
36075
  return String(evalArithExpr(key.expr, row));
36046
36076
  }
36047
- function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
36077
+ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind) {
36048
36078
  if (arg.type === "WILDCARD") {
36049
36079
  return func === "COUNT" ? rows.length : 0;
36050
36080
  }
@@ -36064,6 +36094,7 @@ function evalAggregate(func, distinct, arg, rows, resolveAggSortKind) {
36064
36094
  }
36065
36095
  const eff = distinct ? [...new Set(strValues)] : strValues;
36066
36096
  if (func === "COUNT") return eff.length;
36097
+ if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
36067
36098
  const sortKind = (func === "MIN" || func === "MAX") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
36068
36099
  if (sortKind === "string") {
36069
36100
  if (eff.length === 0) return "";
@@ -36108,7 +36139,7 @@ function minOf(nums) {
36108
36139
  }
36109
36140
  function evalAggArithExpr(node, rows, resolveAggSortKind) {
36110
36141
  if (node.type === "NUMBER") return node.value;
36111
- if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, rows, resolveAggSortKind));
36142
+ if (node.type === "AGG_REF") return Number(evalAggregate(node.func, node.distinct, node.arg, node.separator, rows, resolveAggSortKind));
36112
36143
  const l = evalAggArithExpr(node.left, rows, resolveAggSortKind);
36113
36144
  const r = evalAggArithExpr(node.right, rows, resolveAggSortKind);
36114
36145
  switch (node.op) {
@@ -36457,7 +36488,7 @@ function hasAggregateInStringFuncExpr2(expr) {
36457
36488
  }
36458
36489
  function resolveAggInStringFuncArg(arg, rows, resolveAggSortKind) {
36459
36490
  if (arg.type === "AGG_REF") {
36460
- const value = evalAggregate(arg.func, arg.distinct, arg.arg, rows, resolveAggSortKind);
36491
+ const value = evalAggregate(arg.func, arg.distinct, arg.arg, arg.separator, rows, resolveAggSortKind);
36461
36492
  return typeof value === "number" ? { type: "NUMBER", value } : { type: "STRING", value };
36462
36493
  }
36463
36494
  if (arg.type === "AGG_ARITH") {
@@ -36703,7 +36734,8 @@ function isValidTemporal(value, type) {
36703
36734
  const date5 = new Date(Date.UTC(year, month - 1, day));
36704
36735
  if (date5.getUTCFullYear() !== year || date5.getUTCMonth() !== month - 1 || date5.getUTCDate() !== day) return false;
36705
36736
  if (type === "DATE") return true;
36706
- return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(?:Z|[+-]\d{2}:\d{2})$/.test(value) && isValidTemporal(value.slice(11, value.endsWith("Z") ? -1 : value.length - 6), "TIME");
36737
+ const timePart = value.slice(11, value.endsWith("Z") ? -1 : value.length - 6).replace(/\.\d+$/, "");
36738
+ return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value) && isValidTemporal(timePart, "TIME");
36707
36739
  }
36708
36740
 
36709
36741
  // src/core/dmlValidationCandidates.ts
@@ -36783,6 +36815,8 @@ var SearchAbortedError = class extends Error {
36783
36815
  this.name = "SearchAbortedError";
36784
36816
  }
36785
36817
  };
36818
+ var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
36819
+ var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
36786
36820
  async function execute(sql, client, options = {}) {
36787
36821
  const startedAt = Date.now();
36788
36822
  const stmt = parseSql(sql);
@@ -36928,16 +36962,25 @@ async function executeParsedStatement(stmt, client, options, cacheContext) {
36928
36962
  }
36929
36963
  }
36930
36964
  var TEMP_TABLE_MAX_ROWS = 1e4;
36931
- function appendValidationErrors(tempTables, name, columns, rows, maxRows) {
36965
+ function appendValidationErrors(tempTables, name, columns, rows, maxRows, columnMeta) {
36932
36966
  const current = tempTables.get(name);
36933
- if (current && (current.columns.length !== columns.length || current.columns.some((c, i) => c !== columns[i]))) {
36967
+ if (current && (current.columns.length !== columns.length || current.columns.some((c, i) => c !== columns[i]) || !materializedColumnMetaEqual(current.columnMeta, columnMeta))) {
36934
36968
  throw new Error(`ArgumentError: validation error table ${name} has a different schema.`);
36935
36969
  }
36936
36970
  const existingRows = current?.rows ?? [];
36937
36971
  if (existingRows.length + rows.length > maxRows) {
36938
36972
  throw new Error(`ArgumentError: temp table ${name} exceeds max rows (${maxRows}).`);
36939
36973
  }
36940
- tempTables.set(name, { columns: [...columns], rows: [...existingRows, ...rows] });
36974
+ tempTables.set(name, { columns: [...columns], rows: [...existingRows, ...rows], columnMeta });
36975
+ }
36976
+ function materializedColumnMetaEqual(left, right) {
36977
+ if (left === right) return true;
36978
+ if (!left || !right || left.size !== right.size) return false;
36979
+ for (const [column, meta3] of left) {
36980
+ const candidate = right.get(column);
36981
+ if (!candidate || candidate.sortKind !== meta3.sortKind || candidate.fieldType !== meta3.fieldType) return false;
36982
+ }
36983
+ return true;
36941
36984
  }
36942
36985
  var BatchTimeoutError = class extends Error {
36943
36986
  constructor() {
@@ -37099,7 +37142,8 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
37099
37142
  resolvedStmt.validationErrorTable,
37100
37143
  result.columns,
37101
37144
  result.errors,
37102
- options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS
37145
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
37146
+ materializedMetaByValidationResult.get(result) ?? /* @__PURE__ */ new Map()
37103
37147
  );
37104
37148
  }
37105
37149
  return { result };
@@ -37123,7 +37167,11 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
37123
37167
  onLimitReached: "error"
37124
37168
  };
37125
37169
  const result = await runSelectLike(resolvedStmt.query, client, materializeOptions, cacheContext, tempTables);
37126
- tempTables.set(resolvedStmt.name, { rows: result.rows, columns: result.columns });
37170
+ tempTables.set(resolvedStmt.name, {
37171
+ rows: result.rows,
37172
+ columns: result.columns,
37173
+ columnMeta: materializedMetaBySelectResult.get(result)
37174
+ });
37127
37175
  return { tempTable: resolvedStmt.name, rowCount: result.rows.length };
37128
37176
  }
37129
37177
  if (stmt.type === "DROP_TEMP_TABLE") {
@@ -37159,9 +37207,9 @@ async function executeBatchStatement(stmt, info, client, options, cacheContext,
37159
37207
  }
37160
37208
  async function runSelectLike(query, client, options, cacheContext, tempTables) {
37161
37209
  if (query.type === "WITH") {
37162
- return executeWith(query, client, options, cacheContext, tempTables);
37210
+ return executeWith(query, client, options, cacheContext, tempTables, true);
37163
37211
  }
37164
- return executeQueryWithCte(query, client, options, tempTables, cacheContext);
37212
+ return executeQueryWithCte(query, client, options, tempTables, cacheContext, true);
37165
37213
  }
37166
37214
  async function runWithDeadline(work, remainingMs) {
37167
37215
  if (remainingMs === null) return work;
@@ -37374,18 +37422,27 @@ function evalAssertArith(node) {
37374
37422
  }
37375
37423
  throw new Error(`ArgumentError: unsupported operand in ASSERT expression: ${node.type}`);
37376
37424
  }
37377
- async function executeSelect(stmt, client, options, cacheContext, cteCache) {
37425
+ async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false) {
37426
+ let result;
37378
37427
  if (isNoFromSelect(stmt)) {
37379
- return executeNoFromSelect(stmt);
37428
+ result = executeNoFromSelect(stmt);
37429
+ if (captureColumnMeta) {
37430
+ materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
37431
+ }
37432
+ return result;
37380
37433
  }
37381
37434
  await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
37382
37435
  const mode = resolveSelectMode(stmt);
37383
37436
  await validateSelectFieldCodes(stmt, mode, client, cacheContext);
37384
37437
  if (mode === "SIMPLE") {
37385
- return executeSimpleSelect(stmt, client, options, cacheContext);
37438
+ result = await executeSimpleSelect(stmt, client, options, cacheContext);
37386
37439
  } else {
37387
- return executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
37440
+ result = await executeFullScanSelect(stmt, client, options, cacheContext, cteCache);
37388
37441
  }
37442
+ if (captureColumnMeta) {
37443
+ materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache));
37444
+ }
37445
+ return result;
37389
37446
  }
37390
37447
  function isNoFromSelect(stmt) {
37391
37448
  return stmt.from.appId === 0 && stmt.from.cteName === NO_FROM_CTE_NAME;
@@ -37709,7 +37766,7 @@ function aggregateSortKind(info) {
37709
37766
  if (info.fieldType === "NUMBER" || info.fieldType === "RECORD_NUMBER") return "number";
37710
37767
  return AGGREGATE_STRING_FIELD_TYPES.has(info.fieldType) ? "string" : void 0;
37711
37768
  }
37712
- async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
37769
+ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materializedTables) {
37713
37770
  const refs = collectSelectAggregateSortRefs(stmt.columns);
37714
37771
  if (refs.length === 0) return void 0;
37715
37772
  const appIds = /* @__PURE__ */ new Set();
@@ -37725,11 +37782,9 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
37725
37782
  } else if (stmt.joins.length === 0) {
37726
37783
  if (stmt.from.cteName === null) appIds.add(stmt.from.appId);
37727
37784
  } else {
37728
- if ([stmt.from, ...stmt.joins.map((join) => join.table)].some((table) => table.cteName !== null)) continue;
37729
37785
  for (const table of physicalTables) appIds.add(table.appId);
37730
37786
  }
37731
37787
  }
37732
- if (appIds.size === 0) return void 0;
37733
37788
  const fieldInfosByApp = new Map(
37734
37789
  await Promise.all([...appIds].map(async (appId) => {
37735
37790
  const infos = await getFieldsCached(appId, client, cacheContext);
@@ -37744,17 +37799,28 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext) {
37744
37799
  info = fieldInfosByApp.get(stmt.from.appId)?.get(ref.field);
37745
37800
  } else {
37746
37801
  const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
37747
- if (!table || table.cteName !== null) return void 0;
37802
+ if (!table) return void 0;
37803
+ if (table.cteName !== null) {
37804
+ return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field)?.sortKind;
37805
+ }
37748
37806
  info = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37749
37807
  }
37750
37808
  } else if (stmt.joins.length === 0) {
37751
- if (stmt.from.cteName !== null) return void 0;
37809
+ if (stmt.from.cteName !== null) {
37810
+ return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field)?.sortKind;
37811
+ }
37752
37812
  info = fieldInfosByApp.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
37753
37813
  } 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);
37814
+ const matches = tables.flatMap((table) => {
37815
+ if (table.cteName !== null) {
37816
+ const materialized = materializedTables?.get(table.cteName);
37817
+ return materialized?.columns.includes(ref.field) ? [materialized.columnMeta?.get(ref.field)?.sortKind] : [];
37818
+ }
37819
+ const candidate = fieldInfosByApp.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37820
+ return candidate ? [aggregateSortKind(candidate)] : [];
37821
+ });
37756
37822
  if (matches.length !== 1) return void 0;
37757
- info = matches[0];
37823
+ return matches[0];
37758
37824
  }
37759
37825
  return info ? aggregateSortKind(info) : void 0;
37760
37826
  };
@@ -37763,6 +37829,106 @@ function fieldCodeForTypeLookup(table, field) {
37763
37829
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
37764
37830
  return field;
37765
37831
  }
37832
+ function materializedMetaFromFieldInfo(info) {
37833
+ return { sortKind: aggregateSortKind(info), fieldType: info.fieldType };
37834
+ }
37835
+ function selectNeedsSourceColumnMeta(stmt) {
37836
+ return stmt.columns.some(
37837
+ (column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF"
37838
+ );
37839
+ }
37840
+ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
37841
+ const physicalInfos = /* @__PURE__ */ new Map();
37842
+ if (selectNeedsSourceColumnMeta(stmt)) {
37843
+ await Promise.all(physicalSelectTables(stmt).map(async (table) => {
37844
+ if (physicalInfos.has(table.appId)) return;
37845
+ const infos = await getFieldsCached(table.appId, client, cacheContext);
37846
+ physicalInfos.set(table.appId, new Map(infos.map((info) => [info.code, info])));
37847
+ }));
37848
+ }
37849
+ const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
37850
+ const resolveField2 = (ref) => {
37851
+ if (ref.tableAlias !== null) {
37852
+ if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
37853
+ const info2 = physicalInfos.get(stmt.from.appId)?.get(ref.field);
37854
+ return info2 ? materializedMetaFromFieldInfo(info2) : void 0;
37855
+ }
37856
+ const table = tables.find((candidate) => candidate.alias === ref.tableAlias);
37857
+ if (!table) return void 0;
37858
+ if (table.cteName !== null) return materializedTables?.get(table.cteName)?.columnMeta?.get(ref.field);
37859
+ const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37860
+ return info ? materializedMetaFromFieldInfo(info) : void 0;
37861
+ }
37862
+ if (stmt.joins.length === 0) {
37863
+ if (stmt.from.cteName !== null) return materializedTables?.get(stmt.from.cteName)?.columnMeta?.get(ref.field);
37864
+ const info = physicalInfos.get(stmt.from.appId)?.get(fieldCodeForTypeLookup(stmt.from, ref.field));
37865
+ return info ? materializedMetaFromFieldInfo(info) : void 0;
37866
+ }
37867
+ const matches = tables.flatMap((table) => {
37868
+ if (table.cteName !== null) {
37869
+ const materialized = materializedTables?.get(table.cteName);
37870
+ if (!materialized?.columns.includes(ref.field)) return [];
37871
+ return [materialized.columnMeta?.get(ref.field)];
37872
+ }
37873
+ const info = physicalInfos.get(table.appId)?.get(fieldCodeForTypeLookup(table, ref.field));
37874
+ return info ? [materializedMetaFromFieldInfo(info)] : [];
37875
+ });
37876
+ return matches.length === 1 ? matches[0] : void 0;
37877
+ };
37878
+ const inferred = /* @__PURE__ */ new Map();
37879
+ const hasWildcard = stmt.columns.some((column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD");
37880
+ if (stmt.columns.length === 1 && (stmt.columns[0].type === "WILDCARD" || stmt.columns[0].type === "PARENT_WILDCARD")) {
37881
+ for (const output of outputColumns) {
37882
+ const meta3 = resolveField2(aggregateFieldRef(output));
37883
+ if (meta3) inferred.set(output, meta3);
37884
+ }
37885
+ return inferred;
37886
+ }
37887
+ if (hasWildcard) {
37888
+ for (const output of outputColumns) {
37889
+ const meta3 = resolveField2(aggregateFieldRef(output));
37890
+ if (meta3) inferred.set(output, meta3);
37891
+ }
37892
+ }
37893
+ const explicitColumns = stmt.columns.filter(
37894
+ (column) => column.type !== "WILDCARD" && column.type !== "PARENT_WILDCARD"
37895
+ );
37896
+ explicitColumns.forEach((column, index) => {
37897
+ const output = hasWildcard ? "alias" in column && column.alias ? column.alias : void 0 : outputColumns[index];
37898
+ if (!output) return;
37899
+ let meta3;
37900
+ if (column.type === "FIELD") {
37901
+ meta3 = resolveField2(aggregateFieldRef(column.field));
37902
+ } else if (column.type === "AGGREGATE") {
37903
+ if (column.func === "GROUP_CONCAT") {
37904
+ meta3 = { sortKind: "string" };
37905
+ } else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG") {
37906
+ meta3 = { sortKind: "number" };
37907
+ } else if ((column.func === "MIN" || column.func === "MAX") && column.arg.type === "FIELD_REF") {
37908
+ const source = resolveField2(aggregateFieldRef(column.arg.field));
37909
+ if (source?.sortKind) meta3 = { sortKind: source.sortKind };
37910
+ }
37911
+ } else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
37912
+ meta3 = { sortKind: "number" };
37913
+ } else if (column.type === "LITERAL_COL") {
37914
+ meta3 = { sortKind: "string" };
37915
+ }
37916
+ if (meta3) inferred.set(output, meta3);
37917
+ });
37918
+ return inferred;
37919
+ }
37920
+ function mergeUnionColumnMeta(left, right) {
37921
+ const leftMeta = materializedMetaBySelectResult.get(left);
37922
+ const rightMeta = materializedMetaBySelectResult.get(right);
37923
+ const merged = /* @__PURE__ */ new Map();
37924
+ left.columns.forEach((column, index) => {
37925
+ const a = leftMeta?.get(column);
37926
+ const rightColumn = right.columns[index];
37927
+ const b = rightColumn === void 0 ? void 0 : rightMeta?.get(rightColumn);
37928
+ if (a && b && a.sortKind === b.sortKind && a.fieldType === b.fieldType) merged.set(column, a);
37929
+ });
37930
+ return merged;
37931
+ }
37766
37932
  function buildSelectFieldTypeResolvers(stmt, fieldTypesByApp) {
37767
37933
  const tables = [stmt.from, ...stmt.joins.map((join) => join.table)];
37768
37934
  const physicalTables = tables.filter((table) => table.cteName === null);
@@ -37807,7 +37973,7 @@ async function executeFullScanSelect(stmt, client, options, cacheContext, cteCac
37807
37973
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
37808
37974
  loadTypedPushdownMeta(stmt, client, cacheContext),
37809
37975
  loadTypedInFieldTypes(stmt, client, cacheContext),
37810
- loadAggregateSortKindResolver(stmt, client, cacheContext)
37976
+ loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
37811
37977
  ]);
37812
37978
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
37813
37979
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -37927,9 +38093,9 @@ function deduplicateRows(rows, columns) {
37927
38093
  return true;
37928
38094
  });
37929
38095
  }
37930
- async function executeWith(stmt, client, options, cacheContext, seed) {
38096
+ async function executeWith(stmt, client, options, cacheContext, seed, captureColumnMeta = false) {
37931
38097
  if ((seed == null || seed.size === 0) && canInlineSingleCte(stmt)) {
37932
- return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext);
38098
+ return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext, void 0, captureColumnMeta);
37933
38099
  }
37934
38100
  const cteCache = new Map(seed ?? []);
37935
38101
  for (const cte of stmt.ctes) {
@@ -37939,17 +38105,21 @@ async function executeWith(stmt, client, options, cacheContext, seed) {
37939
38105
  } else if (cte.query.type === "DESCRIBE") {
37940
38106
  result = await executeDescribe(cte.query, client, cacheContext);
37941
38107
  } else {
37942
- result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext);
38108
+ result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext, true);
37943
38109
  }
37944
- cteCache.set(cte.name, { rows: result.rows, columns: result.columns });
38110
+ cteCache.set(cte.name, {
38111
+ rows: result.rows,
38112
+ columns: result.columns,
38113
+ columnMeta: materializedMetaBySelectResult.get(result)
38114
+ });
37945
38115
  }
37946
- return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext);
38116
+ return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext, captureColumnMeta);
37947
38117
  }
37948
- async function executeQueryWithCte(query, client, options, cteCache, cacheContext) {
38118
+ async function executeQueryWithCte(query, client, options, cteCache, cacheContext, captureColumnMeta = false) {
37949
38119
  if (query.type === "UNION") {
37950
38120
  const [leftResult, rightResult] = await Promise.all([
37951
- executeQueryWithCte(query.left, client, options, cteCache, cacheContext),
37952
- executeQueryWithCte(query.right, client, options, cteCache, cacheContext)
38121
+ executeQueryWithCte(query.left, client, options, cteCache, cacheContext, captureColumnMeta),
38122
+ executeQueryWithCte(query.right, client, options, cteCache, cacheContext, captureColumnMeta)
37953
38123
  ]);
37954
38124
  const leftCols = leftResult.columns;
37955
38125
  const rightCols = rightResult.columns;
@@ -37962,13 +38132,21 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
37962
38132
  });
37963
38133
  const combined = [...leftResult.rows, ...remapped];
37964
38134
  const rows = query.all ? combined : deduplicateRows(combined, leftCols);
37965
- return { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
38135
+ const result2 = { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
38136
+ if (captureColumnMeta) {
38137
+ materializedMetaBySelectResult.set(result2, mergeUnionColumnMeta(leftResult, rightResult));
38138
+ }
38139
+ return result2;
37966
38140
  }
37967
38141
  const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
37968
38142
  if (!hasCteRef) {
37969
- return executeSelect(query, client, options, cacheContext, cteCache);
38143
+ return executeSelect(query, client, options, cacheContext, cteCache, captureColumnMeta);
38144
+ }
38145
+ const result = await executeFullScanWithCte(query, client, options, cteCache, cacheContext);
38146
+ if (captureColumnMeta) {
38147
+ materializedMetaBySelectResult.set(result, await inferSelectColumnMeta(query, result.columns, client, cacheContext, cteCache));
37970
38148
  }
37971
- return executeFullScanWithCte(query, client, options, cteCache, cacheContext);
38149
+ return result;
37972
38150
  }
37973
38151
  async function executeFullScanWithCte(stmt, client, options, cteCache, cacheContext) {
37974
38152
  const maxRecords2 = options.maxRecords ?? 1e4;
@@ -37982,7 +38160,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
37982
38160
  const [pushdownMeta, typedInFieldTypes, aggregateSortKindResolver] = await Promise.all([
37983
38161
  loadTypedPushdownMeta(stmt, client, cacheContext),
37984
38162
  loadTypedInFieldTypes(stmt, client, cacheContext),
37985
- loadAggregateSortKindResolver(stmt, client, cacheContext)
38163
+ loadAggregateSortKindResolver(stmt, client, cacheContext, cteCache)
37986
38164
  ]);
37987
38165
  const fieldTypeResolvers = buildSelectFieldTypeResolvers(stmt, typedInFieldTypes);
37988
38166
  const pushdownPlan = buildKlikePushdownPlan(stmt, pushdownMeta);
@@ -38460,7 +38638,23 @@ async function prepareDmlValidation(stmt, client, options, cacheContext, tempTab
38460
38638
  errors,
38461
38639
  ...stmt.validationErrorTable ? { errTable: stmt.validationErrorTable } : stmt.onErrorSkip && stmt.errorTable ? { errTable: stmt.errorTable } : {}
38462
38640
  };
38463
- return { result, candidates, invalidRowNumbers };
38641
+ const columnMeta = /* @__PURE__ */ new Map();
38642
+ for (const column of payloadFields) {
38643
+ if (column === "$id") {
38644
+ columnMeta.set(column, { sortKind: "number", fieldType: "RECORD_NUMBER" });
38645
+ continue;
38646
+ }
38647
+ const info = infoByCode.get(column);
38648
+ if (info) columnMeta.set(column, materializedMetaFromFieldInfo(info));
38649
+ }
38650
+ columnMeta.set("$err_statement", { sortKind: "number" });
38651
+ columnMeta.set("$err_operation", { sortKind: "string" });
38652
+ columnMeta.set("$err_row", { sortKind: "number" });
38653
+ columnMeta.set("$err_field", { sortKind: "string" });
38654
+ columnMeta.set("$err_code", { sortKind: "string" });
38655
+ columnMeta.set("$err_message", { sortKind: "string" });
38656
+ materializedMetaByValidationResult.set(result, columnMeta);
38657
+ return { result, candidates, invalidRowNumbers, columnMeta };
38464
38658
  }
38465
38659
  async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTables, statementNumber) {
38466
38660
  const prepared = await prepareDmlValidation(
@@ -38478,7 +38672,8 @@ async function executeOnErrorSkip(stmt, client, options, cacheContext, tempTable
38478
38672
  errTable,
38479
38673
  prepared.result.columns,
38480
38674
  prepared.result.errors,
38481
- options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS
38675
+ options.tempTableMaxRows ?? TEMP_TABLE_MAX_ROWS,
38676
+ prepared.columnMeta
38482
38677
  );
38483
38678
  const rejectLimit = stmt.rejectLimit ?? null;
38484
38679
  if (rejectLimit !== null && prepared.result.invalidRows > rejectLimit) {
@@ -42219,7 +42414,7 @@ Options:
42219
42414
  -h, --help Show help
42220
42415
  `);
42221
42416
  }
42222
- var SERVER_VERSION = true ? "2.14.1" : "0.0.0-dev";
42417
+ var SERVER_VERSION = true ? "2.15.0" : "0.0.0-dev";
42223
42418
  function createServer(args) {
42224
42419
  const server = new McpServer({
42225
42420
  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.14.1",
3
+ "version": "2.15.0",
4
4
  "description": "kintone SQL plugin, CLI, and MCP tools (ksql)",
5
5
  "publishConfig": {
6
6
  "access": "public"