@rex0220/kintone-sql-tools 3.15.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cli/ksql.js CHANGED
@@ -584,6 +584,120 @@ function numberLiteralText(node) {
584
584
  return toPlainDecimal(source) ?? source;
585
585
  }
586
586
 
587
+ // src/core/aggregateExpression.ts
588
+ function quote(value) {
589
+ return `'${value.replace(/'/g, "''")}'`;
590
+ }
591
+ function arithLabel(node, topLevel = false) {
592
+ if (node.type === "FIELD_REF") return node.field;
593
+ if (node.type === "NUMBER") return numberLiteralText(node);
594
+ if (node.type === "STRING_FUNC") return stringFuncLabel(node);
595
+ const label = `${arithLabel(node.left)}${node.op}${arithLabel(node.right)}`;
596
+ return topLevel ? label : `(${label})`;
597
+ }
598
+ function fieldValueLabel(value) {
599
+ if (value.type === "FIELD") return value.tableAlias ? `${value.tableAlias}.${value.field}` : value.field;
600
+ if (value.type === "FUNC_FIELD") return stringFuncLabel(value.expr);
601
+ if (value.type === "ARITH_FIELD") return arithLabel(value.expr);
602
+ return caseLabel(value.expr);
603
+ }
604
+ function sqlValueLabel(value) {
605
+ switch (value.type) {
606
+ case "STRING":
607
+ return quote(value.value);
608
+ case "NUMBER":
609
+ return numberLiteralText(value);
610
+ case "VARIABLE":
611
+ return `@${value.name}`;
612
+ case "VARIABLE_IN_LIST":
613
+ return `@${value.name}`;
614
+ case "KINTONE_FUNC":
615
+ return `${value.name}()`;
616
+ case "ARRAY":
617
+ return `[${value.elements.map((entry) => quote(entry.value)).join(",")}]`;
618
+ case "IN_LIST":
619
+ return `(${value.values.map(sqlValueLabel).join(",")})`;
620
+ case "ARITH_VALUE":
621
+ return arithLabel(value.expr);
622
+ case "CASE_VALUE":
623
+ return caseLabel(value.expr);
624
+ case "SUBQUERY_IN_LIST":
625
+ return "(SUBQUERY)";
626
+ case "SCALAR_SUBQUERY":
627
+ return "(SUBQUERY)";
628
+ }
629
+ }
630
+ function whereLabel(expr) {
631
+ switch (expr.type) {
632
+ case "BINARY":
633
+ return `${fieldValueLabel(expr.left)} ${expr.op.replace("_", " ")} ${sqlValueLabel(expr.right)}`;
634
+ case "NULL_CHECK":
635
+ return `${fieldValueLabel(expr.field)} IS ${expr.not ? "NOT " : ""}NULL`;
636
+ case "LOGICAL":
637
+ return `(${whereLabel(expr.left)} ${expr.op} ${whereLabel(expr.right)})`;
638
+ case "NOT":
639
+ return `NOT (${whereLabel(expr.expr)})`;
640
+ case "GROUP":
641
+ return `(${whereLabel(expr.expr)})`;
642
+ case "BOOLEAN":
643
+ return expr.value ? "TRUE" : "FALSE";
644
+ case "EXISTS":
645
+ return `${expr.not ? "NOT " : ""}EXISTS (SUBQUERY)`;
646
+ }
647
+ }
648
+ function caseResultLabel(result) {
649
+ if (result.type === "ARRAY") return `[${result.elements.map((entry) => quote(entry.value)).join(",")}]`;
650
+ if (result.type === "FIELD_REF" || result.type === "ARITH") return arithLabel(result);
651
+ return scalarValueLabel(result);
652
+ }
653
+ function caseLabel(expr) {
654
+ const branches = expr.branches.map((branch) => `WHEN ${whereLabel(branch.condition)} THEN ${caseResultLabel(branch.result)}`).join(" ");
655
+ const otherwise = expr.elseResult === null ? "" : ` ELSE ${caseResultLabel(expr.elseResult)}`;
656
+ return `CASE ${branches}${otherwise} END`;
657
+ }
658
+ function stringFuncArgLabel(arg) {
659
+ if (arg.type === "AGG_REF") return aggregateSyntheticName(arg.func, arg.distinct, arg.arg);
660
+ if (arg.type === "AGG_ARITH") return aggregateOperandLabel(arg);
661
+ return scalarValueLabel(arg);
662
+ }
663
+ function stringFuncLabel(expr) {
664
+ return `${expr.func}(${expr.args.map(stringFuncArgLabel).join(",")})`;
665
+ }
666
+ function scalarValueLabel(expr) {
667
+ switch (expr.type) {
668
+ case "STRING":
669
+ return quote(expr.value);
670
+ case "NUMBER":
671
+ return numberLiteralText(expr);
672
+ case "VARIABLE":
673
+ return `@${expr.name}`;
674
+ case "FIELD":
675
+ return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
676
+ case "STRING_FUNC":
677
+ return stringFuncLabel(expr);
678
+ case "CASE_WHEN":
679
+ return caseLabel(expr);
680
+ case "SCALAR_ARITH":
681
+ return `(${scalarValueLabel(expr.left)}${expr.op}${scalarValueLabel(expr.right)})`;
682
+ case "CONCAT_OP":
683
+ return `(${scalarValueLabel(expr.left)}||${scalarValueLabel(expr.right)})`;
684
+ }
685
+ }
686
+ function aggregateArgLabel(arg) {
687
+ if (arg.type === "WILDCARD") return "*";
688
+ if (arg.type === "FIELD_REF" || arg.type === "ARITH") return arithLabel(arg, true);
689
+ return scalarValueLabel(arg);
690
+ }
691
+ function aggregateSyntheticName(func, distinct, arg) {
692
+ const label = aggregateArgLabel(arg);
693
+ return distinct ? `${func}(DISTINCT ${label})` : `${func}(${label})`;
694
+ }
695
+ function aggregateOperandLabel(node) {
696
+ if (node.type === "NUMBER") return numberLiteralText(node);
697
+ if (node.type === "AGG_REF") return aggregateSyntheticName(node.func, node.distinct, node.arg);
698
+ return `${aggregateOperandLabel(node.left)}${node.op}${aggregateOperandLabel(node.right)}`;
699
+ }
700
+
587
701
  // src/parser/parser.ts
588
702
  var MAX_BATCH_STATEMENTS = 20;
589
703
  var PARSER_SCALAR_FUNCTION_TOKEN_MAP = Object.freeze({
@@ -787,6 +901,7 @@ var Parser = class {
787
901
  this.scalarAllowsAggregateArgs = true;
788
902
  this.scalarAllowsCase = true;
789
903
  this.pos = 0;
904
+ this.insideAggregateArg = 0;
790
905
  /** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
791
906
  this.cteNames = /* @__PURE__ */ new Set();
792
907
  /** パース中に出現した一時テーブル参照(#name)のトークン。単文 API での拒否に使う */
@@ -1615,7 +1730,7 @@ var Parser = class {
1615
1730
  if (this.consume("*" /* STAR */)) {
1616
1731
  return { type: "WILDCARD" };
1617
1732
  }
1618
- if (this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
1733
+ if (this.tryAggregateFunc() === null && this.hasTopLevelTokenBeforeValueEnd("||" /* CONCAT_OP */)) {
1619
1734
  const expr = this.parseScalarValueExpr({ allowAggregateArgs: true });
1620
1735
  const alias2 = this.consume("AS" /* AS */) ? this.parseAliasName() : null;
1621
1736
  return { type: "SCALAR_VALUE_COL", expr, alias: alias2 };
@@ -1887,7 +2002,12 @@ var Parser = class {
1887
2002
  if (!allowCase) throw new ParseError("\u3053\u306E\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u3067\u306F CASE \u3092\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
1888
2003
  return this.parseCaseWhenExpr();
1889
2004
  }
1890
- if (this.tryAggregateFunc() !== null) throw new ParseError("\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
2005
+ if (this.tryAggregateFunc() !== null) {
2006
+ throw new ParseError(
2007
+ this.insideAggregateArg > 0 ? "\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5185\u306B\u96C6\u8A08\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093" : "\u30B9\u30AB\u30E9\u30FC\u5024\u5F0F\u306B\u96C6\u7D04\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
2008
+ tok
2009
+ );
2010
+ }
1891
2011
  if (this.tryStringFuncName() !== null) return this.parseStringFuncExpr();
1892
2012
  if (tok.kind === "IDENT" /* IDENT */ || tok.kind === "BIDENT" /* BIDENT */) {
1893
2013
  this.advance();
@@ -2036,6 +2156,9 @@ var Parser = class {
2036
2156
  /** THEN / ELSE の結果値。`||` を含む場合だけ新スカラー文法へ渡す。 */
2037
2157
  parseCaseResult() {
2038
2158
  const tok = this.peek();
2159
+ if (this.insideAggregateArg > 0 && this.tryAggregateFunc() !== null) {
2160
+ throw new ParseError("\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5185\u306B\u96C6\u8A08\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", tok);
2161
+ }
2039
2162
  if (tok.kind === "[" /* LBRACKET */) {
2040
2163
  return this.parseArrayLiteral();
2041
2164
  }
@@ -2179,7 +2302,7 @@ var Parser = class {
2179
2302
  }
2180
2303
  arg = { type: "WILDCARD" };
2181
2304
  } else {
2182
- arg = this.parseArithAddSub();
2305
+ arg = this.parseAggregateArgExpr();
2183
2306
  }
2184
2307
  let separator;
2185
2308
  if (this.isSoftKeyword("SEPARATOR")) {
@@ -2198,6 +2321,60 @@ var Parser = class {
2198
2321
  ...separator !== void 0 ? { separator } : {}
2199
2322
  };
2200
2323
  }
2324
+ isAggregateArgEnd() {
2325
+ return this.peek().kind === ")" /* RPAREN */ || this.isSoftKeyword("SEPARATOR");
2326
+ }
2327
+ /** 旧算術 AST を優先し、新規形だけ ScalarValueExpr として読む。 */
2328
+ parseAggregateArgExpr() {
2329
+ const start = this.pos;
2330
+ try {
2331
+ const legacy = this.parseArithAddSub();
2332
+ if (this.isAggregateArgEnd()) return legacy;
2333
+ } catch {
2334
+ }
2335
+ this.pos = start;
2336
+ this.insideAggregateArg++;
2337
+ try {
2338
+ let expr;
2339
+ try {
2340
+ expr = this.parseScalarValueExpr({ allowCase: true, allowAggregateArgs: false });
2341
+ } catch (error) {
2342
+ if (error instanceof ParseError) {
2343
+ if (error.message.includes("\u6BD4\u8F03\u30FB\u8FF0\u8A9E")) {
2344
+ throw new ParseError(
2345
+ "\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u306B\u6BD4\u8F03\u30FB\u8FF0\u8A9E\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002CASE \u3067\u5024\u3092\u660E\u793A\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B: SUM(CASE WHEN amount > 0 THEN 1 ELSE 0 END)",
2346
+ error.token
2347
+ );
2348
+ }
2349
+ if (error.message.includes("\u96C6\u7D04\u95A2\u6570")) {
2350
+ throw new ParseError("\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5185\u306B\u96C6\u8A08\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", error.token);
2351
+ }
2352
+ }
2353
+ throw error;
2354
+ }
2355
+ if (!this.isAggregateArgEnd()) {
2356
+ throw new ParseError(
2357
+ "\u3053\u306E\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5F62\u5F0F\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002CASE \u3067\u5024\u3092\u660E\u793A\u3059\u308B\u304B\u3001CTE \u3067\u5F0F\u3092\u5217\u306B\u3057\u3066\u304B\u3089\u96C6\u8A08\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2358
+ this.peek()
2359
+ );
2360
+ }
2361
+ this.assertNoNestedAggregate(expr);
2362
+ return expr;
2363
+ } finally {
2364
+ this.insideAggregateArg--;
2365
+ }
2366
+ }
2367
+ assertNoNestedAggregate(expr) {
2368
+ const visit = (value) => {
2369
+ if (value === null || typeof value !== "object") return;
2370
+ const node = value;
2371
+ if (node.type === "AGG_REF" || node.type === "AGG_ARITH") {
2372
+ throw new ParseError("\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5185\u306B\u96C6\u8A08\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
2373
+ }
2374
+ for (const child of Object.values(node)) visit(child);
2375
+ };
2376
+ visit(expr);
2377
+ }
2201
2378
  // ----------------------------------------------------------
2202
2379
  // FROM / JOIN
2203
2380
  // ----------------------------------------------------------
@@ -2466,31 +2643,11 @@ var Parser = class {
2466
2643
  }
2467
2644
  const aggFunc = this.tryAggregateFunc();
2468
2645
  if (aggFunc !== null) {
2469
- this.advance();
2470
- this.expect("(" /* LPAREN */);
2471
- const distinct = this.consume("DISTINCT" /* DISTINCT */);
2472
- const distinctToken = distinct ? this.prev() : null;
2473
- if (aggFunc === "MODE" && distinctToken) {
2474
- throw new ParseError("MODE \u3067\u306F DISTINCT \u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", distinctToken);
2475
- }
2476
- let argStr;
2477
- if (this.consume("*" /* STAR */)) {
2478
- if (!aggregateAcceptsWildcard(aggFunc)) {
2479
- throw new ParseError(`${aggFunc}(*) \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());
2480
- }
2481
- argStr = "*";
2482
- } else {
2483
- argStr = this.parseIdentifier();
2484
- }
2485
- if (this.isSoftKeyword("SEPARATOR")) {
2486
- const separatorToken = this.advance();
2487
- if (aggFunc !== "GROUP_CONCAT") {
2488
- throw new ParseError("SEPARATOR \u306F GROUP_CONCAT \u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059", separatorToken);
2489
- }
2490
- this.expect("STRING" /* STRING */, "SEPARATOR \u306E\u5F8C\u306B\u306F\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u304C\u5FC5\u8981\u3067\u3059");
2646
+ if (this.insideAggregateArg > 0) {
2647
+ throw new ParseError("\u96C6\u8A08\u95A2\u6570\u306E\u5F15\u6570\u5185\u306B\u96C6\u8A08\u95A2\u6570\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093", this.peek());
2491
2648
  }
2492
- this.expect(")" /* RPAREN */);
2493
- const syntheticName = distinct ? `${aggFunc}(DISTINCT ${argStr})` : `${aggFunc}(${argStr})`;
2649
+ const ref = this.parseAggregateRef(aggFunc);
2650
+ const syntheticName = aggregateSyntheticName(ref.func, ref.distinct, ref.arg);
2494
2651
  return { type: "FIELD", tableAlias: null, field: syntheticName };
2495
2652
  }
2496
2653
  if (this.peek().kind === "CASE" /* CASE */) {
@@ -4631,7 +4788,7 @@ function collectCaseResultScalarFields(result, out) {
4631
4788
  }
4632
4789
  function collectAggOperandFields(node, out) {
4633
4790
  if (node.type === "AGG_REF") {
4634
- if (node.arg.type !== "WILDCARD") collectArithNode(node.arg, out);
4791
+ if (node.arg.type !== "WILDCARD") collectAggregateArgFields(node.arg, out);
4635
4792
  return;
4636
4793
  }
4637
4794
  if (node.type === "AGG_ARITH") {
@@ -4639,6 +4796,10 @@ function collectAggOperandFields(node, out) {
4639
4796
  collectAggOperandFields(node.right, out);
4640
4797
  }
4641
4798
  }
4799
+ function collectAggregateArgFields(node, out) {
4800
+ if (node.type === "FIELD_REF" || node.type === "ARITH") collectArithNode(node, out);
4801
+ else collectScalarValueFields(node, out);
4802
+ }
4642
4803
  function hasAggregateInStringFuncExpr(expr) {
4643
4804
  return expr.args.some((arg) => {
4644
4805
  if (arg.type === "AGG_REF" || arg.type === "AGG_ARITH") return true;
@@ -4784,7 +4945,7 @@ function collectRequiredFieldsByTable(stmt) {
4784
4945
  };
4785
4946
  const walkAgg = (node, phase = "select") => {
4786
4947
  if (node.type === "AGG_REF") {
4787
- if (node.arg.type !== "WILDCARD") walkArith(node.arg, phase);
4948
+ if (node.arg.type !== "WILDCARD") walkAggregateArg(node.arg, phase);
4788
4949
  return;
4789
4950
  }
4790
4951
  if (node.type === "AGG_ARITH") {
@@ -4818,6 +4979,13 @@ function collectRequiredFieldsByTable(stmt) {
4818
4979
  }
4819
4980
  if (expr.type === "CASE_WHEN") walkCase(expr, phase);
4820
4981
  };
4982
+ const walkAggregateArg = (expr, phase = "select") => {
4983
+ if (expr.type === "FIELD_REF" || expr.type === "ARITH") {
4984
+ walkArith(expr, phase);
4985
+ return;
4986
+ }
4987
+ walkScalar(expr, phase);
4988
+ };
4821
4989
  const walkCaseResult = (result, phase = "select") => {
4822
4990
  if (result.type === "ARRAY") return;
4823
4991
  if (result.type === "FIELD_REF" || result.type === "ARITH") {
@@ -4919,7 +5087,7 @@ function collectRequiredFieldsByTable(stmt) {
4919
5087
  case "VARIABLE_COL":
4920
5088
  throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
4921
5089
  case "AGGREGATE":
4922
- if (col.arg.type !== "WILDCARD") walkArith(col.arg, "select");
5090
+ if (col.arg.type !== "WILDCARD") walkAggregateArg(col.arg, "select");
4923
5091
  break;
4924
5092
  case "ARITH_AGG_COL":
4925
5093
  walkAgg(col.expr, "select");
@@ -5000,44 +5168,6 @@ function collectSelectOutputNames(columns) {
5000
5168
  }
5001
5169
  return names;
5002
5170
  }
5003
- function aggregateSyntheticName(func, distinct, arg) {
5004
- const argStr = arg.type === "WILDCARD" ? "*" : arithNodeLabel(arg);
5005
- return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
5006
- }
5007
- function arithNodeLabel(node) {
5008
- if (node.type === "FIELD_REF") return node.field;
5009
- if (node.type === "NUMBER") return numberLiteralText(node);
5010
- if (node.type === "STRING_FUNC") return stringFuncLabel(node);
5011
- return `(${arithNodeLabel(node.left)}${node.op}${arithNodeLabel(node.right)})`;
5012
- }
5013
- function stringFuncLabel(expr) {
5014
- const args = expr.args.map((a) => {
5015
- if (a.type === "AGG_REF") return aggregateSyntheticName(a.func, a.distinct, a.arg);
5016
- if (a.type === "AGG_ARITH") return "agg_arith";
5017
- return scalarValueLabel(a);
5018
- });
5019
- return `${expr.func}(${args.join(",")})`;
5020
- }
5021
- function scalarValueLabel(expr) {
5022
- switch (expr.type) {
5023
- case "STRING":
5024
- return `'${expr.value}'`;
5025
- case "NUMBER":
5026
- return numberLiteralText(expr);
5027
- case "VARIABLE":
5028
- return `@${expr.name}`;
5029
- case "FIELD":
5030
- return expr.tableAlias ? `${expr.tableAlias}.${expr.field}` : expr.field;
5031
- case "STRING_FUNC":
5032
- return stringFuncLabel(expr);
5033
- case "CASE_WHEN":
5034
- return "case";
5035
- case "SCALAR_ARITH":
5036
- return `(${scalarValueLabel(expr.left)}${expr.op}${scalarValueLabel(expr.right)})`;
5037
- case "CONCAT_OP":
5038
- return `(${scalarValueLabel(expr.left)}||${scalarValueLabel(expr.right)})`;
5039
- }
5040
- }
5041
5171
  function isAggregateSyntheticName(name) {
5042
5172
  return /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/i.test(name);
5043
5173
  }
@@ -6059,6 +6189,38 @@ function evalScalarValueExpr(expr, row, resolveFieldType, resolveFieldSemantics2
6059
6189
  }
6060
6190
  }
6061
6191
  }
6192
+ function evalScalarValueExprNullable(expr, row, resolveFieldType, resolveFieldSemantics2) {
6193
+ switch (expr.type) {
6194
+ case "CASE_WHEN":
6195
+ return evalCaseWhenNullable(expr, row, resolveFieldType, resolveFieldSemantics2);
6196
+ case "SCALAR_ARITH": {
6197
+ const left = evalScalarValueExprNullable(expr.left, row, resolveFieldType, resolveFieldSemantics2);
6198
+ const right = evalScalarValueExprNullable(expr.right, row, resolveFieldType, resolveFieldSemantics2);
6199
+ if (left === null || right === null) return null;
6200
+ const l = Number(left);
6201
+ const r = Number(right);
6202
+ switch (expr.op) {
6203
+ case "+":
6204
+ return l + r;
6205
+ case "-":
6206
+ return l - r;
6207
+ case "*":
6208
+ return l * r;
6209
+ case "/":
6210
+ return r !== 0 ? l / r : NaN;
6211
+ case "%":
6212
+ return r !== 0 ? l % r : NaN;
6213
+ }
6214
+ }
6215
+ case "CONCAT_OP": {
6216
+ const left = evalScalarValueExprNullable(expr.left, row, resolveFieldType, resolveFieldSemantics2);
6217
+ const right = evalScalarValueExprNullable(expr.right, row, resolveFieldType, resolveFieldSemantics2);
6218
+ return `${left ?? ""}${right ?? ""}`;
6219
+ }
6220
+ default:
6221
+ return evalScalarValueExpr(expr, row, resolveFieldType, resolveFieldSemantics2);
6222
+ }
6223
+ }
6062
6224
  function applyRoundOp(op, num, digits) {
6063
6225
  const factor = Math.pow(10, digits);
6064
6226
  const raw = Math[op](num * factor) / factor;
@@ -6768,6 +6930,20 @@ function evalCaseWhen(expr, row, resolveFieldType, resolveFieldSemantics2) {
6768
6930
  }
6769
6931
  return "";
6770
6932
  }
6933
+ function evalCaseWhenNullable(expr, row, resolveFieldType, resolveFieldSemantics2) {
6934
+ for (const branch of expr.branches) {
6935
+ if (evalWhere(branch.condition, row, resolveFieldType, void 0, resolveFieldSemantics2)) {
6936
+ return evalCaseResultNullable(branch.result, row, resolveFieldType, resolveFieldSemantics2);
6937
+ }
6938
+ }
6939
+ return expr.elseResult === null ? null : evalCaseResultNullable(expr.elseResult, row, resolveFieldType, resolveFieldSemantics2);
6940
+ }
6941
+ function evalCaseResultNullable(result, row, resolveFieldType, resolveFieldSemantics2) {
6942
+ if (result.type === "ARRAY") return result.elements.map((entry) => entry.value).join(",");
6943
+ if (result.type === "FIELD_REF") return row[result.field] ?? "";
6944
+ if (result.type === "ARITH") return evalArithExpr(result, row);
6945
+ return evalScalarValueExprNullable(result, row, resolveFieldType, resolveFieldSemantics2);
6946
+ }
6771
6947
  function evalCaseResult(result, row, resolveFieldType, resolveFieldSemantics2) {
6772
6948
  if (result.type === "ARRAY") return result.elements.map((e) => e.value).join(",");
6773
6949
  if (result.type === "FIELD_REF") {
@@ -7001,7 +7177,7 @@ function collectScalarValueFields2(expr, out) {
7001
7177
  }
7002
7178
  function collectAggOperandFields2(node, out) {
7003
7179
  if (node.type === "AGG_REF") {
7004
- if (node.arg.type !== "WILDCARD") collectArithNode2(node.arg, out);
7180
+ if (node.arg.type !== "WILDCARD") collectAggregateArgFields2(node.arg, out);
7005
7181
  return;
7006
7182
  }
7007
7183
  if (node.type === "AGG_ARITH") {
@@ -7009,6 +7185,10 @@ function collectAggOperandFields2(node, out) {
7009
7185
  collectAggOperandFields2(node.right, out);
7010
7186
  }
7011
7187
  }
7188
+ function collectAggregateArgFields2(node, out) {
7189
+ if (node.type === "FIELD_REF" || node.type === "ARITH") collectArithNode2(node, out);
7190
+ else collectScalarValueFields2(node, out);
7191
+ }
7012
7192
  function collectCaseResultFields(result, out) {
7013
7193
  if (result.type === "ARRAY") return;
7014
7194
  if (result.type === "FIELD_REF" || result.type === "ARITH") {
@@ -9999,7 +10179,7 @@ function applyGroupBy(rows, groupByKeys, columns, resolveAggSortKind) {
9999
10179
  }
10000
10180
  for (const col of columns) {
10001
10181
  if (col.type === "AGGREGATE") {
10002
- const syntheticKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
10182
+ const syntheticKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
10003
10183
  const value = String(evalAggregate(col.func, col.distinct, col.arg, col.separator, groupRows, resolveAggSortKind));
10004
10184
  outRow[col.alias ?? syntheticKey] = value;
10005
10185
  if (col.alias) outRow[syntheticKey] = value;
@@ -10036,10 +10216,16 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
10036
10216
  const raw = row[arg.field];
10037
10217
  if (raw === void 0 || raw === "" && func !== "MIN" && func !== "MAX") continue;
10038
10218
  strVal = raw;
10039
- } else {
10219
+ } else if (arg.type === "ARITH" || arg.type === "NUMBER" || arg.type === "STRING_FUNC") {
10040
10220
  const n = evalArithExpr(arg, row);
10041
10221
  if (isNaN(n)) continue;
10042
10222
  strVal = String(n);
10223
+ } else {
10224
+ const value = evalScalarValueExprNullable(arg, row);
10225
+ if (value === null) continue;
10226
+ if (value === "" && func !== "MIN" && func !== "MAX") continue;
10227
+ if (typeof value === "number" && Number.isNaN(value)) continue;
10228
+ strVal = String(value);
10043
10229
  }
10044
10230
  strValues.push(strVal);
10045
10231
  }
@@ -10054,8 +10240,8 @@ function evalAggregate(func, distinct, arg, separator, rows, resolveAggSortKind)
10054
10240
  const eff = distinct ? statistical ? [...new Set(numericValues)] : [...new Set(strValues)] : statistical ? numericValues : strValues;
10055
10241
  if (func === "COUNT") return eff.length;
10056
10242
  if (func === "GROUP_CONCAT") return eff.join(separator ?? ",");
10057
- const comparison = (func === "MIN" || func === "MAX" || func === "MODE") && arg.type === "FIELD_REF" ? resolveAggSortKind?.(toAggregateFieldRef(arg.field)) : void 0;
10058
- const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" ? syntheticSemantics("string") : syntheticSemantics("number"));
10243
+ const comparison = func === "MIN" || func === "MAX" || func === "MODE" ? resolveAggregateArgSemantics(arg, resolveAggSortKind) : void 0;
10244
+ const semantics = typeof comparison === "string" ? syntheticSemantics(comparison) : comparison ?? (arg.type === "FIELD_REF" || arg.type === "FIELD" || arg.type === "STRING" || arg.type === "CONCAT_OP" || arg.type === "CASE_WHEN" ? syntheticSemantics("string") : syntheticSemantics("number"));
10059
10245
  if (func === "MODE") {
10060
10246
  if (strValues.length === 0) return "";
10061
10247
  const frequencies = /* @__PURE__ */ new Map();
@@ -10143,17 +10329,24 @@ function evalAggArithExpr(node, rows, resolveAggSortKind) {
10143
10329
  }
10144
10330
  }
10145
10331
  function aggArithDefaultKey(node) {
10146
- if (node.type === "NUMBER") return numberLiteralText(node);
10147
- if (node.type === "AGG_REF") return aggregateSyntheticName2(node.func, node.distinct, node.arg);
10148
- return `${aggArithDefaultKey(node.left)}${node.op}${aggArithDefaultKey(node.right)}`;
10332
+ return aggregateOperandLabel(node);
10149
10333
  }
10150
- function aggregateArgLabel(arg) {
10151
- if (arg.type === "WILDCARD") return "*";
10152
- return arithColDefaultKey(arg);
10153
- }
10154
- function aggregateSyntheticName2(func, distinct, arg) {
10155
- const argStr = aggregateArgLabel(arg);
10156
- return distinct ? `${func}(DISTINCT ${argStr})` : `${func}(${argStr})`;
10334
+ function resolveAggregateArgSemantics(arg, resolver) {
10335
+ if (arg.type === "FIELD_REF") return resolver?.(toAggregateFieldRef(arg.field)) ?? "string";
10336
+ if (arg.type === "FIELD") return resolver?.(arg) ?? "string";
10337
+ if (arg.type === "NUMBER" || arg.type === "ARITH" || arg.type === "SCALAR_ARITH") return "number";
10338
+ if (arg.type === "STRING" || arg.type === "CONCAT_OP" || arg.type === "VARIABLE") return "string";
10339
+ if (arg.type === "STRING_FUNC") {
10340
+ const numeric = /* @__PURE__ */ new Set(["LENGTH", "LENGTH_CHAR", "INSTR", "ROUND", "FLOOR", "CEIL", "TRUNCATE", "YEAR", "MONTH", "DAY", "DATEDIFF", "ABS", "MOD", "POWER", "SQRT", "DAYOFWEEK", "QUARTER", "WEEK"]);
10341
+ if (arg.func === "CAST") return arg.args[1]?.type === "STRING" && arg.args[1].value === "NUMBER" ? "number" : "string";
10342
+ return numeric.has(arg.func) ? "number" : "string";
10343
+ }
10344
+ const results = [...arg.branches.map((branch) => branch.result), ...arg.elseResult === null ? [] : [arg.elseResult]].filter((result) => result.type !== "ARRAY").map((result) => resolveAggregateArgSemantics(result, resolver));
10345
+ if (results.length === 0 || results.some((result) => result === void 0)) return "string";
10346
+ const kinds = results.map((result) => typeof result === "string" ? result : result.compareMode === "number" || result.compareMode === "recordNumber" ? "number" : "string");
10347
+ if (!kinds.every((kind) => kind === kinds[0])) return "string";
10348
+ const first = results[0];
10349
+ return results.every((result) => JSON.stringify(result) === JSON.stringify(first)) ? first : kinds[0];
10157
10350
  }
10158
10351
  function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
10159
10352
  if (having === null) return rows;
@@ -10299,7 +10492,7 @@ function buildOrderByAliasEvaluator(columns, scalarCache, resolveFieldType, reso
10299
10492
  evaluators.set(alias, () => column.value);
10300
10493
  break;
10301
10494
  case "AGGREGATE": {
10302
- const source = aggregateSyntheticName2(column.func, column.distinct, column.arg);
10495
+ const source = aggregateSyntheticName(column.func, column.distinct, column.arg);
10303
10496
  evaluators.set(alias, (row) => row[alias] ?? row[source] ?? "0");
10304
10497
  break;
10305
10498
  }
@@ -10424,7 +10617,7 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
10424
10617
  break;
10425
10618
  }
10426
10619
  case "AGGREGATE": {
10427
- const srcKey = aggregateSyntheticName2(col.func, col.distinct, col.arg);
10620
+ const srcKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
10428
10621
  const dstKey = outputKeys?.[colIdx] ?? col.alias ?? srcKey;
10429
10622
  out[dstKey] = row[col.alias ?? srcKey] ?? row[srcKey] ?? "0";
10430
10623
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(dstKey);
@@ -10510,7 +10703,7 @@ function computeOutputKey(col, colIdx, defaultFieldKeys) {
10510
10703
  case "LITERAL_COL":
10511
10704
  return col.alias ?? `'${col.value}'`;
10512
10705
  case "AGGREGATE":
10513
- return col.alias ?? aggregateSyntheticName2(col.func, col.distinct, col.arg);
10706
+ return col.alias ?? aggregateSyntheticName(col.func, col.distinct, col.arg);
10514
10707
  case "ARITH_AGG_COL":
10515
10708
  return col.alias ?? aggArithDefaultKey(col.expr);
10516
10709
  case "ARITH_COL":
@@ -13110,7 +13303,14 @@ async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, mate
13110
13303
  };
13111
13304
  }
13112
13305
  function selectCaseConditionsNeedFieldMetadata(stmt) {
13113
- return stmt.columns.some((column) => column.type === "CASE_COL" && column.expr.branches.some((branch) => whereNeedsFieldMetadata(branch.condition)));
13306
+ const visit = (value) => {
13307
+ if (value === null || typeof value !== "object") return false;
13308
+ if (Array.isArray(value)) return value.some(visit);
13309
+ const node = value;
13310
+ if (node.type === "CASE_WHEN" && node.branches?.some((branch) => whereNeedsFieldMetadata(branch.condition))) return true;
13311
+ return Object.values(node).some(visit);
13312
+ };
13313
+ return stmt.columns.some(visit);
13114
13314
  }
13115
13315
  function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
13116
13316
  const aliases = /* @__PURE__ */ new Map();
@@ -13122,7 +13322,17 @@ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
13122
13322
  semantics = syntheticSemantics("number");
13123
13323
  } else if (column.type === "AGGREGATE") {
13124
13324
  if (column.func === "MIN" || column.func === "MAX" || column.func === "MODE") {
13125
- semantics = column.arg.type === "FIELD_REF" ? rowResolver(aggregateFieldRef(column.arg.field)) : syntheticSemantics("number");
13325
+ if (column.arg.type !== "WILDCARD") {
13326
+ semantics = inferAggregateArgMeta(column.arg, (ref) => {
13327
+ const resolved = rowResolver(ref);
13328
+ if (!resolved) return void 0;
13329
+ return {
13330
+ sortKind: resolved.compareMode === "number" || resolved.compareMode === "recordNumber" ? "number" : "string",
13331
+ fieldType: resolved.fieldType,
13332
+ semantics: resolved
13333
+ };
13334
+ }).semantics;
13335
+ }
13126
13336
  } else {
13127
13337
  semantics = column.func === "GROUP_CONCAT" ? syntheticSemantics("string") : syntheticSemantics("number");
13128
13338
  }
@@ -13571,8 +13781,38 @@ function aggregateFieldRef(field) {
13571
13781
  return dot > 0 ? { type: "FIELD", tableAlias: field.slice(0, dot), field: field.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field };
13572
13782
  }
13573
13783
  function collectAggregateRef(func, arg, out) {
13574
- if ((func === "MIN" || func === "MAX" || func === "MODE") && arg.type === "FIELD_REF" && arg.field) {
13784
+ if (func !== "MIN" && func !== "MAX" && func !== "MODE" || arg.type === "WILDCARD") return;
13785
+ collectAggregateArgFieldRefs(arg, out);
13786
+ }
13787
+ function collectAggregateArgFieldRefs(arg, out) {
13788
+ if (arg.type === "FIELD_REF") {
13575
13789
  out.push(aggregateFieldRef(arg.field));
13790
+ return;
13791
+ }
13792
+ if (arg.type === "FIELD") {
13793
+ out.push(arg);
13794
+ return;
13795
+ }
13796
+ if (arg.type === "ARITH") {
13797
+ collectAggregateArgFieldRefs(arg.left, out);
13798
+ collectAggregateArgFieldRefs(arg.right, out);
13799
+ return;
13800
+ }
13801
+ if (arg.type === "SCALAR_ARITH" || arg.type === "CONCAT_OP") {
13802
+ collectAggregateArgFieldRefs(arg.left, out);
13803
+ collectAggregateArgFieldRefs(arg.right, out);
13804
+ return;
13805
+ }
13806
+ if (arg.type === "STRING_FUNC") {
13807
+ for (const child of arg.args) {
13808
+ if (child.type !== "AGG_REF" && child.type !== "AGG_ARITH") collectAggregateArgFieldRefs(child, out);
13809
+ }
13810
+ return;
13811
+ }
13812
+ if (arg.type === "CASE_WHEN") {
13813
+ for (const result of [...arg.branches.map((branch) => branch.result), ...arg.elseResult ? [arg.elseResult] : []]) {
13814
+ if (result.type !== "ARRAY") collectAggregateArgFieldRefs(result, out);
13815
+ }
13576
13816
  }
13577
13817
  }
13578
13818
  function collectAggregateOperandRefs(node, out) {
@@ -13683,7 +13923,7 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
13683
13923
  const base = info.semantics ?? resolveFieldSemantics(info);
13684
13924
  return info.fieldType === "STATUS" && statusOrdersByApp.has(appId) ? { ...base, optionOrder: statusOrdersByApp.get(appId) } : base;
13685
13925
  };
13686
- return (ref) => {
13926
+ const resolveRef = (ref) => {
13687
13927
  let info;
13688
13928
  if (ref.tableAlias !== null) {
13689
13929
  if (ref.tableAlias === "_p" && stmt.from.subtableCode && stmt.from.cteName === null) {
@@ -13717,6 +13957,7 @@ async function loadAggregateSortKindResolver(stmt, client, cacheContext, materia
13717
13957
  const sourceTable = ref.tableAlias !== null ? tables.find((table) => effectiveTableAlias(table) === ref.tableAlias) : stmt.joins.length === 0 ? stmt.from : void 0;
13718
13958
  return semanticsForInfo(info, sourceTable?.appId ?? stmt.from.appId);
13719
13959
  };
13960
+ return resolveRef;
13720
13961
  }
13721
13962
  function fieldCodeForTypeLookup(table, field) {
13722
13963
  if (table.subtableCode && field.startsWith("_p.")) return field.slice(3);
@@ -13826,9 +14067,19 @@ function mergeExpressionColumnMeta(candidates) {
13826
14067
  }
13827
14068
  return unknownStringColumnMeta();
13828
14069
  }
14070
+ function inferAggregateArgMeta(arg, resolveField2) {
14071
+ if (arg.type === "FIELD_REF") return resolveField2(aggregateFieldRef(arg.field)) ?? unknownStringColumnMeta();
14072
+ if (arg.type === "FIELD") return resolveField2(arg) ?? unknownStringColumnMeta();
14073
+ if (arg.type === "NUMBER" || arg.type === "ARITH" || arg.type === "SCALAR_ARITH") return syntheticColumnMeta("number");
14074
+ if (arg.type === "STRING" || arg.type === "CONCAT_OP" || arg.type === "VARIABLE") return syntheticColumnMeta("string");
14075
+ if (arg.type === "STRING_FUNC") return stringFunctionColumnMeta(arg);
14076
+ const results = arg.branches.map((branch) => caseResultColumnMeta(branch.result, resolveField2));
14077
+ if (arg.elseResult) results.push(caseResultColumnMeta(arg.elseResult, resolveField2));
14078
+ return mergeExpressionColumnMeta(results);
14079
+ }
13829
14080
  function selectNeedsSourceColumnMeta(stmt) {
13830
14081
  return stmt.columns.some(
13831
- (column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "CASE_COL" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX" || column.func === "MODE") && column.arg.type === "FIELD_REF"
14082
+ (column) => column.type === "FIELD" || column.type === "WILDCARD" || column.type === "PARENT_WILDCARD" || column.type === "CASE_COL" || column.type === "AGGREGATE" && (column.func === "MIN" || column.func === "MAX" || column.func === "MODE")
13832
14083
  );
13833
14084
  }
13834
14085
  async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext, materializedTables) {
@@ -13899,11 +14150,8 @@ async function inferSelectColumnMeta(stmt, outputColumns, client, cacheContext,
13899
14150
  meta = syntheticColumnMeta("string");
13900
14151
  } else if (column.func === "COUNT" || column.func === "SUM" || column.func === "AVG" || column.func === "STDDEV_POP" || column.func === "STDDEV_SAMP" || column.func === "VAR_POP" || column.func === "VAR_SAMP" || column.func === "MEDIAN") {
13901
14152
  meta = syntheticColumnMeta("number");
13902
- } else if ((column.func === "MIN" || column.func === "MAX" || column.func === "MODE") && column.arg.type === "FIELD_REF") {
13903
- const source = resolveField2(aggregateFieldRef(column.arg.field));
13904
- if (source) meta = source;
13905
- } else if (column.func === "MODE") {
13906
- meta = syntheticColumnMeta("number");
14153
+ } else if ((column.func === "MIN" || column.func === "MAX" || column.func === "MODE") && column.arg.type !== "WILDCARD") {
14154
+ meta = inferAggregateArgMeta(column.arg, resolveField2);
13907
14155
  }
13908
14156
  } else if (column.type === "ARITH_AGG_COL" || column.type === "ARITH_COL") {
13909
14157
  meta = syntheticColumnMeta("number");
@@ -14669,7 +14917,7 @@ async function buildOrderSemanticsForSelect(stmt, client, cacheContext, material
14669
14917
  meta = mergeExpressionColumnMeta(candidates);
14670
14918
  } else if (column.type === "AGGREGATE") {
14671
14919
  if (column.func === "MIN" || column.func === "MAX" || column.func === "MODE") {
14672
- meta = column.arg.type === "FIELD_REF" ? resolveField2(aggregateFieldRef(column.arg.field)) : syntheticColumnMeta("number");
14920
+ if (column.arg.type !== "WILDCARD") meta = inferAggregateArgMeta(column.arg, resolveField2);
14673
14921
  } else {
14674
14922
  meta = column.func === "GROUP_CONCAT" ? syntheticColumnMeta("string") : syntheticColumnMeta("number");
14675
14923
  }