@rex0220/kintone-sql-tools 3.17.1 → 3.18.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,87 @@ function numberLiteralText(node) {
584
584
  return toPlainDecimal(source) ?? source;
585
585
  }
586
586
 
587
+ // src/core/grouping.ts
588
+ var B65_MAX_GROUPING_SETS = 64;
589
+ var B65_MAX_GROUPING_ITEMS = 16;
590
+ var B65_MAX_GENERATED_ROWS = 5e4;
591
+ function expandCubeGroupingSets(items) {
592
+ let expandedSetCount = 1;
593
+ for (const _item of items) {
594
+ if (expandedSetCount > Math.floor(B65_MAX_GROUPING_SETS / 2)) {
595
+ const rejectedSetCount = expandedSetCount * 2;
596
+ throw new Error(
597
+ `ArgumentError: B65 expanded grouping set count ${rejectedSetCount} exceeds limit ${B65_MAX_GROUPING_SETS} (reason=GROUPING_SET_LIMIT_EXCEEDED).`
598
+ );
599
+ }
600
+ expandedSetCount *= 2;
601
+ }
602
+ let sets = [{ items: [] }];
603
+ for (const item of items) {
604
+ sets = sets.flatMap((set) => [
605
+ { items: [...set.items, item] },
606
+ { items: [...set.items] }
607
+ ]);
608
+ }
609
+ return sets;
610
+ }
611
+ function groupingFieldSyntaxKey(item) {
612
+ return `${item.tableAlias ?? ""}\0${item.field}`;
613
+ }
614
+ function normalizeGroupingSpec(stmt) {
615
+ if (stmt.grouping !== void 0 && stmt.groupBy.length > 0) {
616
+ throw new Error("internal error: SELECT cannot contain both groupBy and grouping.");
617
+ }
618
+ if (stmt.grouping === void 0) {
619
+ return stmt.groupBy.length === 0 ? { type: "NONE" } : { type: "PLAIN", allItems: stmt.groupBy, sets: [stmt.groupBy] };
620
+ }
621
+ const seen = /* @__PURE__ */ new Set();
622
+ const allItems = [];
623
+ for (const set of stmt.grouping.sets) {
624
+ for (const item of set.items) {
625
+ const key = groupingFieldSyntaxKey(item);
626
+ if (seen.has(key)) continue;
627
+ seen.add(key);
628
+ allItems.push(item);
629
+ }
630
+ }
631
+ const sets = stmt.grouping.sets.map((set) => ({ items: [...set.items] }));
632
+ return {
633
+ type: "GROUPING_SETS",
634
+ source: stmt.grouping.source,
635
+ allItems,
636
+ sets
637
+ };
638
+ }
639
+ function hasGroupingClause(stmt) {
640
+ return normalizeGroupingSpec(stmt).type !== "NONE";
641
+ }
642
+ function resolveGroupingSpec(stmt, resolve2) {
643
+ const normalized = normalizeGroupingSpec(stmt);
644
+ if (normalized.type !== "GROUPING_SETS") return null;
645
+ const byCanonicalId = /* @__PURE__ */ new Map();
646
+ const resolveItem = (field) => {
647
+ const resolved = resolve2(field);
648
+ const existing = byCanonicalId.get(resolved.canonicalId);
649
+ if (existing) return existing;
650
+ const item = { ...resolved, field };
651
+ byCanonicalId.set(item.canonicalId, item);
652
+ return item;
653
+ };
654
+ const allItems = normalized.allItems.map(resolveItem).filter(
655
+ (item, index, items) => items.findIndex((candidate) => candidate.canonicalId === item.canonicalId) === index
656
+ );
657
+ const sets = normalized.sets.map((set) => ({
658
+ items: set.items.map(resolveItem)
659
+ }));
660
+ return {
661
+ type: "GROUPING_SETS",
662
+ source: normalized.source,
663
+ allItems,
664
+ sets
665
+ };
666
+ }
667
+
587
668
  // src/core/aggregateExpression.ts
588
669
  function quote(value) {
589
670
  return `'${value.replace(/'/g, "''")}'`;
@@ -906,8 +987,8 @@ var Parser = class {
906
987
  this.scalarAllowsCase = true;
907
988
  this.pos = 0;
908
989
  this.insideAggregateArg = 0;
909
- /** GROUPING(field) is only legal while parsing a SELECT CASE condition. */
910
- this.groupingFieldAllowedDepth = 0;
990
+ /** GROUPING(field) is limited to the explicitly selected query context. */
991
+ this.groupingFieldContext = "FORBIDDEN";
911
992
  /** WITH 句で定義された CTE 名のセット(parseTableRef で参照) */
912
993
  this.cteNames = /* @__PURE__ */ new Set();
913
994
  /** パース中に出現した一時テーブル参照(#name)のトークン。単文 API での拒否に使う */
@@ -1652,7 +1733,7 @@ var Parser = class {
1652
1733
  } else if (this.isRollupStart()) {
1653
1734
  grouping = this.parseRollupClause();
1654
1735
  } else if (this.isCubeStart()) {
1655
- throw new ParseError("B65: CUBE is not supported in Phase1.", this.peek());
1736
+ grouping = this.parseCubeClause();
1656
1737
  } else {
1657
1738
  groupBy = this.parseGroupByKeys();
1658
1739
  }
@@ -1660,7 +1741,7 @@ var Parser = class {
1660
1741
  throw new ParseError("B65: ordinary GROUP BY items cannot be mixed with grouping elements.", this.peek());
1661
1742
  }
1662
1743
  if (this.consume("HAVING" /* HAVING */)) {
1663
- having = this.parseWhereExpr();
1744
+ having = this.parseWhereExpr("HAVING");
1664
1745
  }
1665
1746
  }
1666
1747
  let orderMode = "CANONICAL";
@@ -2188,12 +2269,7 @@ var Parser = class {
2188
2269
  }
2189
2270
  parseCaseCondition(allowGroupingCondition) {
2190
2271
  if (!allowGroupingCondition) return this.parseWhereExpr();
2191
- this.groupingFieldAllowedDepth++;
2192
- try {
2193
- return this.parseWhereExpr();
2194
- } finally {
2195
- this.groupingFieldAllowedDepth--;
2196
- }
2272
+ return this.parseWhereExpr("SELECT_CASE");
2197
2273
  }
2198
2274
  /** THEN / ELSE の結果値。`||` を含む場合だけ新スカラー文法へ渡す。 */
2199
2275
  parseCaseResult() {
@@ -2510,8 +2586,14 @@ var Parser = class {
2510
2586
  // ----------------------------------------------------------
2511
2587
  // WHERE 式(再帰下降・優先順位付き)
2512
2588
  // ----------------------------------------------------------
2513
- parseWhereExpr() {
2514
- return this.parseOrExpr();
2589
+ parseWhereExpr(groupingFieldContext = this.groupingFieldContext) {
2590
+ const previousContext = this.groupingFieldContext;
2591
+ this.groupingFieldContext = groupingFieldContext;
2592
+ try {
2593
+ return this.parseOrExpr();
2594
+ } finally {
2595
+ this.groupingFieldContext = previousContext;
2596
+ }
2515
2597
  }
2516
2598
  // OR(最低優先度)
2517
2599
  parseOrExpr() {
@@ -2680,9 +2762,9 @@ var Parser = class {
2680
2762
  throw new ParseError("B65: GROUPING_ID is not supported in Phase1.", this.peek());
2681
2763
  }
2682
2764
  if (this.isGroupingFunctionStart()) {
2683
- if (this.groupingFieldAllowedDepth === 0) {
2765
+ if (this.groupingFieldContext === "FORBIDDEN" || this.insideAggregateArg > 0) {
2684
2766
  throw new ParseError(
2685
- "B65: GROUPING() is only allowed in SELECT, SELECT CASE conditions, and direct ORDER BY.",
2767
+ "B65: GROUPING() is only allowed in SELECT, SELECT CASE conditions, HAVING, and direct ORDER BY.",
2686
2768
  this.peek()
2687
2769
  );
2688
2770
  }
@@ -2942,6 +3024,25 @@ var Parser = class {
2942
3024
  sets
2943
3025
  };
2944
3026
  }
3027
+ parseCubeClause() {
3028
+ this.advance();
3029
+ this.expect("(" /* LPAREN */);
3030
+ if (this.peek().kind === ")" /* RPAREN */) {
3031
+ throw new ParseError("B65: CUBE requires at least one field.", this.peek());
3032
+ }
3033
+ const items = [];
3034
+ do {
3035
+ items.push(this.parseGroupingFieldItem());
3036
+ } while (this.consume("," /* COMMA */));
3037
+ this.expect(")" /* RPAREN */);
3038
+ const sets = expandCubeGroupingSets(items);
3039
+ return {
3040
+ type: "GROUPING_SETS",
3041
+ source: "CUBE",
3042
+ allItems: this.groupingAllItems(sets),
3043
+ sets
3044
+ };
3045
+ }
2945
3046
  parseGroupingRef() {
2946
3047
  const start = this.advance();
2947
3048
  this.expect("(" /* LPAREN */);
@@ -3930,67 +4031,6 @@ function extractTableRef(name, tok) {
3930
4031
  return { appId: Number(m[1]), subtableCode: m[2] ?? null };
3931
4032
  }
3932
4033
 
3933
- // src/core/grouping.ts
3934
- var B65_MAX_GROUPING_SETS = 64;
3935
- var B65_MAX_GROUPING_ITEMS = 16;
3936
- var B65_MAX_GENERATED_ROWS = 5e4;
3937
- function groupingFieldSyntaxKey(item) {
3938
- return `${item.tableAlias ?? ""}\0${item.field}`;
3939
- }
3940
- function normalizeGroupingSpec(stmt) {
3941
- if (stmt.grouping !== void 0 && stmt.groupBy.length > 0) {
3942
- throw new Error("internal error: SELECT cannot contain both groupBy and grouping.");
3943
- }
3944
- if (stmt.grouping === void 0) {
3945
- return stmt.groupBy.length === 0 ? { type: "NONE" } : { type: "PLAIN", allItems: stmt.groupBy, sets: [stmt.groupBy] };
3946
- }
3947
- const seen = /* @__PURE__ */ new Set();
3948
- const allItems = [];
3949
- for (const set of stmt.grouping.sets) {
3950
- for (const item of set.items) {
3951
- const key = groupingFieldSyntaxKey(item);
3952
- if (seen.has(key)) continue;
3953
- seen.add(key);
3954
- allItems.push(item);
3955
- }
3956
- }
3957
- const sets = stmt.grouping.sets.map((set) => ({ items: [...set.items] }));
3958
- return {
3959
- type: "GROUPING_SETS",
3960
- source: stmt.grouping.source,
3961
- allItems,
3962
- sets
3963
- };
3964
- }
3965
- function hasGroupingClause(stmt) {
3966
- return normalizeGroupingSpec(stmt).type !== "NONE";
3967
- }
3968
- function resolveGroupingSpec(stmt, resolve2) {
3969
- const normalized = normalizeGroupingSpec(stmt);
3970
- if (normalized.type !== "GROUPING_SETS") return null;
3971
- const byCanonicalId = /* @__PURE__ */ new Map();
3972
- const resolveItem = (field) => {
3973
- const resolved = resolve2(field);
3974
- const existing = byCanonicalId.get(resolved.canonicalId);
3975
- if (existing) return existing;
3976
- const item = { ...resolved, field };
3977
- byCanonicalId.set(item.canonicalId, item);
3978
- return item;
3979
- };
3980
- const allItems = normalized.allItems.map(resolveItem).filter(
3981
- (item, index, items) => items.findIndex((candidate) => candidate.canonicalId === item.canonicalId) === index
3982
- );
3983
- const sets = normalized.sets.map((set) => ({
3984
- items: set.items.map(resolveItem)
3985
- }));
3986
- return {
3987
- type: "GROUPING_SETS",
3988
- source: normalized.source,
3989
- allItems,
3990
- sets
3991
- };
3992
- }
3993
-
3994
4034
  // src/core/dmlGuard.ts
3995
4035
  function getStatementType(stmt) {
3996
4036
  if (!stmt || typeof stmt !== "object") return "UNKNOWN";
@@ -5926,6 +5966,272 @@ function isSelectObject(value) {
5926
5966
  return value !== null && typeof value === "object" && value.type === "SELECT";
5927
5967
  }
5928
5968
 
5969
+ // src/engine/groupingRowMeta.ts
5970
+ var groupingRowMetaKey = /* @__PURE__ */ Symbol("ksql.groupingRowMeta");
5971
+ var groupingRefCanonicalIds = /* @__PURE__ */ new WeakMap();
5972
+ function attachGroupingRowMeta(row, includedCanonicalIds) {
5973
+ Object.defineProperty(row, groupingRowMetaKey, {
5974
+ value: { includedCanonicalIds },
5975
+ enumerable: false,
5976
+ configurable: false,
5977
+ writable: false
5978
+ });
5979
+ return row;
5980
+ }
5981
+ function getGroupingRowMeta(row) {
5982
+ return row[groupingRowMetaKey];
5983
+ }
5984
+ function readGroupingMembership(row) {
5985
+ return getGroupingRowMeta(row)?.includedCanonicalIds;
5986
+ }
5987
+ function bindGroupingRefCanonicalId(ref, canonicalId) {
5988
+ groupingRefCanonicalIds.set(ref, canonicalId);
5989
+ }
5990
+ function evalGroupingRef(ref, row) {
5991
+ const membership = readGroupingMembership(row);
5992
+ if (!membership) {
5993
+ throw new Error("internal error: GROUPING() evaluation requires B65 grouping row membership.");
5994
+ }
5995
+ const canonicalId = groupingRefCanonicalIds.get(ref);
5996
+ if (!canonicalId) {
5997
+ throw new Error("internal error: GROUPING() reference was not resolved during B65 planning.");
5998
+ }
5999
+ return membership.has(canonicalId) ? "0" : "1";
6000
+ }
6001
+
6002
+ // src/core/groupingValidation.ts
6003
+ var enforceGroupingPlanningCandidateLimits = (facts) => {
6004
+ if (facts.expandedSetCount > B65_MAX_GROUPING_SETS) {
6005
+ throw new Error(
6006
+ `ArgumentError: B65 expanded grouping set count ${facts.expandedSetCount} exceeds limit ${B65_MAX_GROUPING_SETS} (reason=GROUPING_SET_LIMIT_EXCEEDED).`
6007
+ );
6008
+ }
6009
+ if (facts.canonicalItemCount > B65_MAX_GROUPING_ITEMS) {
6010
+ throw new Error(
6011
+ `ArgumentError: B65 canonical grouping item count ${facts.canonicalItemCount} exceeds limit ${B65_MAX_GROUPING_ITEMS} (reason=GROUPING_ITEM_LIMIT_EXCEEDED).`
6012
+ );
6013
+ }
6014
+ };
6015
+ function displayField(field) {
6016
+ return field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
6017
+ }
6018
+ function refFromName(name) {
6019
+ const dot = name.indexOf(".");
6020
+ return dot > 0 ? { type: "FIELD", tableAlias: name.slice(0, dot), field: name.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: name };
6021
+ }
6022
+ function collectGroupingRefs(node, out) {
6023
+ if (node === null || typeof node !== "object") return;
6024
+ if (Array.isArray(node)) {
6025
+ node.forEach((item) => collectGroupingRefs(item, out));
6026
+ return;
6027
+ }
6028
+ const value = node;
6029
+ if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
6030
+ if (value["type"] === "GROUPING_REF") {
6031
+ out.push(value);
6032
+ return;
6033
+ }
6034
+ Object.values(value).forEach((item) => collectGroupingRefs(item, out));
6035
+ }
6036
+ function collectAggregateArgumentGroupingRefs(node, out) {
6037
+ if (node === null || typeof node !== "object") return;
6038
+ if (Array.isArray(node)) {
6039
+ node.forEach((item) => collectAggregateArgumentGroupingRefs(item, out));
6040
+ return;
6041
+ }
6042
+ const value = node;
6043
+ if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
6044
+ if (value["type"] === "AGGREGATE" || value["type"] === "AGG_REF") {
6045
+ collectGroupingRefs(value["arg"], out);
6046
+ return;
6047
+ }
6048
+ Object.values(value).forEach((item) => collectAggregateArgumentGroupingRefs(item, out));
6049
+ }
6050
+ function collectNonAggregateFieldRefs(node, out) {
6051
+ if (node === null || typeof node !== "object") return;
6052
+ if (Array.isArray(node)) {
6053
+ node.forEach((item) => collectNonAggregateFieldRefs(item, out));
6054
+ return;
6055
+ }
6056
+ const value = node;
6057
+ const type = value["type"];
6058
+ if (type === "SELECT" || type === "SCALAR_SUBQUERY" || type === "GROUPING_REF" || type === "AGG_REF" || type === "AGG_ARITH") return;
6059
+ if (type === "FIELD" && typeof value["field"] === "string") {
6060
+ out.push({
6061
+ type: "FIELD",
6062
+ tableAlias: typeof value["tableAlias"] === "string" ? value["tableAlias"] : null,
6063
+ field: value["field"]
6064
+ });
6065
+ return;
6066
+ }
6067
+ if (type === "FIELD_REF" && typeof value["field"] === "string") {
6068
+ out.push(refFromName(value["field"]));
6069
+ return;
6070
+ }
6071
+ Object.values(value).forEach((item) => collectNonAggregateFieldRefs(item, out));
6072
+ }
6073
+ function containsAggregate(node) {
6074
+ if (node === null || typeof node !== "object") return false;
6075
+ if (Array.isArray(node)) return node.some(containsAggregate);
6076
+ const value = node;
6077
+ if (value["type"] === "AGG_REF" || value["type"] === "AGG_ARITH") return true;
6078
+ if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
6079
+ return Object.values(value).some(containsAggregate);
6080
+ }
6081
+ function isAggregateMaterializedAlias(column) {
6082
+ if (!("alias" in column) || column.alias === null) return false;
6083
+ if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
6084
+ if (column.type === "STRFUNC_COL" || column.type === "SCALAR_VALUE_COL") {
6085
+ return containsAggregate(column);
6086
+ }
6087
+ return false;
6088
+ }
6089
+ function outputAliases(columns) {
6090
+ return new Set(columns.flatMap(
6091
+ (column) => "alias" in column && typeof column.alias === "string" ? [column.alias] : []
6092
+ ));
6093
+ }
6094
+ function isAggregateSyntheticReference(ref) {
6095
+ return ref.tableAlias === null && /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/.test(ref.field);
6096
+ }
6097
+ function validateGroupingRefMembership(ref, resolve2, canonicalItems) {
6098
+ const resolved = resolve2(ref.field);
6099
+ if (!resolved.physical) {
6100
+ throw new Error(`ArgumentError: B65 grouping reference ${displayField(ref.field)} must resolve to a physical APP field.`);
6101
+ }
6102
+ if (!canonicalItems.has(resolved.canonicalId)) {
6103
+ throw new Error(
6104
+ `ArgumentError: B65 GROUPING argument ${displayField(ref.field)} is not present in grouping allItems (reason=B65_GROUPING_ARG_NOT_ITEM).`
6105
+ );
6106
+ }
6107
+ bindGroupingRefCanonicalId(ref, resolved.canonicalId);
6108
+ }
6109
+ function validateDependency(ref, resolve2, canonicalItems, context) {
6110
+ const resolved = resolve2(ref);
6111
+ if (!resolved.physical || !canonicalItems.has(resolved.canonicalId)) {
6112
+ throw new Error(
6113
+ `ArgumentError: B65 non-aggregate field ${displayField(ref)} in ${context} is not a grouping item (reason=B65_NON_GROUPED_DEPENDENCY).`
6114
+ );
6115
+ }
6116
+ }
6117
+ function keyDependencies(key) {
6118
+ if (key.type === "GROUPING_KEY") return [];
6119
+ if (key.type === "FIELD_NAME") return [refFromName(key.name)];
6120
+ const refs = [];
6121
+ collectNonAggregateFieldRefs(key, refs);
6122
+ return refs;
6123
+ }
6124
+ function validateGroupingStatic(stmt) {
6125
+ const normalized = normalizeGroupingSpec(stmt);
6126
+ const groupingRefs = [];
6127
+ for (const column of stmt.columns) {
6128
+ if (column.type !== "WINDOW_COL") collectGroupingRefs(column, groupingRefs);
6129
+ }
6130
+ collectGroupingRefs(stmt.having, groupingRefs);
6131
+ collectGroupingRefs(stmt.orderBy, groupingRefs);
6132
+ const forbiddenGroupingRefs = [];
6133
+ collectGroupingRefs(stmt.where, forbiddenGroupingRefs);
6134
+ collectGroupingRefs(stmt.joins, forbiddenGroupingRefs);
6135
+ collectAggregateArgumentGroupingRefs(stmt.columns, forbiddenGroupingRefs);
6136
+ collectAggregateArgumentGroupingRefs(stmt.having, forbiddenGroupingRefs);
6137
+ for (const column of stmt.columns) {
6138
+ if (column.type === "WINDOW_COL") collectGroupingRefs(column, forbiddenGroupingRefs);
6139
+ }
6140
+ if (forbiddenGroupingRefs.length > 0) {
6141
+ throw new Error(
6142
+ "ArgumentError: B65 GROUPING() is not allowed in WHERE, JOIN, window, aggregate arguments, or DML expressions."
6143
+ );
6144
+ }
6145
+ if (normalized.type !== "GROUPING_SETS") {
6146
+ if (groupingRefs.length > 0) {
6147
+ throw new Error("ArgumentError: B65 GROUPING() requires GROUP BY ROLLUP or GROUPING SETS.");
6148
+ }
6149
+ return;
6150
+ }
6151
+ if (stmt.orderMode === "KINTONE_NATIVE") {
6152
+ throw new Error("ArgumentError: B65 KORDER BY is not supported in Phase1.");
6153
+ }
6154
+ if (stmt.columns.some((column) => column.type === "WINDOW_COL")) {
6155
+ throw new Error("ArgumentError: B65 window functions are not supported in Phase1.");
6156
+ }
6157
+ if (stmt.columns.some(
6158
+ (column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD"
6159
+ )) {
6160
+ throw new Error("ArgumentError: B65 wildcard projection is not supported in Phase1.");
6161
+ }
6162
+ }
6163
+ function validateGroupingPlanning(stmt, resolve2, planningGuardHook = () => void 0) {
6164
+ validateGroupingStatic(stmt);
6165
+ const normalized = normalizeGroupingSpec(stmt);
6166
+ const groupingRefs = [];
6167
+ for (const column of stmt.columns) {
6168
+ if (column.type !== "WINDOW_COL") collectGroupingRefs(column, groupingRefs);
6169
+ }
6170
+ collectGroupingRefs(stmt.having, groupingRefs);
6171
+ collectGroupingRefs(stmt.orderBy, groupingRefs);
6172
+ if (normalized.type !== "GROUPING_SETS") {
6173
+ return null;
6174
+ }
6175
+ const resolvedSpec = resolveGroupingSpec(stmt, resolve2);
6176
+ const canonicalItems = /* @__PURE__ */ new Set();
6177
+ const resolvedItems = [];
6178
+ for (const item of resolvedSpec.allItems) {
6179
+ const resolved = item;
6180
+ if (!resolved.physical) {
6181
+ throw new Error(`ArgumentError: B65 grouping item ${displayField(item.field)} must resolve to a physical APP field.`);
6182
+ }
6183
+ if (!canonicalItems.has(resolved.canonicalId)) {
6184
+ canonicalItems.add(resolved.canonicalId);
6185
+ resolvedItems.push(resolved);
6186
+ }
6187
+ }
6188
+ planningGuardHook({
6189
+ expandedSetCount: normalized.sets.length,
6190
+ canonicalItemCount: canonicalItems.size
6191
+ });
6192
+ for (const ref of groupingRefs) {
6193
+ validateGroupingRefMembership(ref, resolve2, canonicalItems);
6194
+ }
6195
+ const aliases = outputAliases(stmt.columns);
6196
+ for (const column of stmt.columns) {
6197
+ if (column.type === "GROUPING_COL" || column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL" || column.type === "LITERAL_COL" || column.type === "VARIABLE_COL" || column.type === "SCALAR_SUBQUERY_COL") continue;
6198
+ const refs = [];
6199
+ if (column.type === "FIELD") refs.push(refFromName(column.field));
6200
+ else collectNonAggregateFieldRefs(column, refs);
6201
+ for (const ref of refs) validateDependency(ref, resolve2, canonicalItems, "SELECT");
6202
+ }
6203
+ if (stmt.having) {
6204
+ const refs = [];
6205
+ collectNonAggregateFieldRefs(stmt.having, refs);
6206
+ for (const ref of refs) {
6207
+ if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
6208
+ validateDependency(ref, resolve2, canonicalItems, "HAVING");
6209
+ }
6210
+ }
6211
+ for (const order of stmt.orderBy) {
6212
+ for (const ref of keyDependencies(order.key)) {
6213
+ if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
6214
+ validateDependency(ref, resolve2, canonicalItems, "ORDER BY");
6215
+ }
6216
+ }
6217
+ const collisionKeys = /* @__PURE__ */ new Set();
6218
+ for (const item of resolvedItems) {
6219
+ collisionKeys.add(item.directKey);
6220
+ if (item.unqualifiedBridgeKey !== null) collisionKeys.add(item.unqualifiedBridgeKey);
6221
+ }
6222
+ for (const column of stmt.columns) {
6223
+ if (!isAggregateMaterializedAlias(column)) continue;
6224
+ const alias = "alias" in column ? column.alias : null;
6225
+ if (alias === null) continue;
6226
+ if (collisionKeys.has(alias)) {
6227
+ throw new Error(
6228
+ `ArgumentError: B65 aggregate alias ${alias} collides with a grouping runtime key (reason=B65_AGGREGATE_ALIAS_COLLISION).`
6229
+ );
6230
+ }
6231
+ }
6232
+ return resolvedSpec;
6233
+ }
6234
+
5929
6235
  // src/core/batch.ts
5930
6236
  var MAX_TEMP_TABLES = 16;
5931
6237
  var MAX_BATCH_VARIABLES = 64;
@@ -5967,11 +6273,32 @@ function collectVariableRefs(node, refs) {
5967
6273
  for (const v of Object.values(obj)) collectVariableRefs(v, refs);
5968
6274
  }
5969
6275
  }
6276
+ function validateGroupingStaticQueries(node) {
6277
+ if (Array.isArray(node)) {
6278
+ for (const value of node) validateGroupingStaticQueries(value);
6279
+ return;
6280
+ }
6281
+ if (node !== null && typeof node === "object") {
6282
+ const obj = node;
6283
+ if (obj["type"] === "SELECT") {
6284
+ validateGroupingStatic(node);
6285
+ }
6286
+ for (const value of Object.values(obj)) validateGroupingStaticQueries(value);
6287
+ }
6288
+ }
5970
6289
  function analyzeBatch(statements) {
5971
6290
  if (statements.length === 0) {
5972
6291
  throw new BatchAnalysisError("ArgumentError: SQL is empty.", 0);
5973
6292
  }
5974
6293
  statements.forEach((stmt, index) => {
6294
+ try {
6295
+ validateGroupingStaticQueries(stmt);
6296
+ } catch (error) {
6297
+ if (error instanceof Error) {
6298
+ throw new BatchAnalysisError(error.message, index);
6299
+ }
6300
+ throw error;
6301
+ }
5975
6302
  try {
5976
6303
  assertApplyScope("phase15b", stmt);
5977
6304
  validateKlikeStatement(stmt);
@@ -6975,39 +7302,6 @@ function resolveFieldRef(row, field) {
6975
7302
  return "";
6976
7303
  }
6977
7304
 
6978
- // src/engine/groupingRowMeta.ts
6979
- var groupingRowMetaKey = /* @__PURE__ */ Symbol("ksql.groupingRowMeta");
6980
- var groupingRefCanonicalIds = /* @__PURE__ */ new WeakMap();
6981
- function attachGroupingRowMeta(row, includedCanonicalIds) {
6982
- Object.defineProperty(row, groupingRowMetaKey, {
6983
- value: { includedCanonicalIds },
6984
- enumerable: false,
6985
- configurable: false,
6986
- writable: false
6987
- });
6988
- return row;
6989
- }
6990
- function getGroupingRowMeta(row) {
6991
- return row[groupingRowMetaKey];
6992
- }
6993
- function readGroupingMembership(row) {
6994
- return getGroupingRowMeta(row)?.includedCanonicalIds;
6995
- }
6996
- function bindGroupingRefCanonicalId(ref, canonicalId) {
6997
- groupingRefCanonicalIds.set(ref, canonicalId);
6998
- }
6999
- function evalGroupingRef(ref, row) {
7000
- const membership = readGroupingMembership(row);
7001
- if (!membership) {
7002
- throw new Error("internal error: GROUPING() evaluation requires B65 grouping row membership.");
7003
- }
7004
- const canonicalId = groupingRefCanonicalIds.get(ref);
7005
- if (!canonicalId) {
7006
- throw new Error("internal error: GROUPING() reference was not resolved during B65 planning.");
7007
- }
7008
- return membership.has(canonicalId) ? "0" : "1";
7009
- }
7010
-
7011
7305
  // src/engine/evalWhere.ts
7012
7306
  function evalWhere(expr, row, resolveFieldType, appliedKlikes, resolveFieldSemantics2) {
7013
7307
  switch (expr.type) {
@@ -9949,212 +10243,6 @@ function explainNeedsAppMetadata(statement) {
9949
10243
  return visit(statement);
9950
10244
  }
9951
10245
 
9952
- // src/core/groupingValidation.ts
9953
- var enforceGroupingPlanningCandidateLimits = (facts) => {
9954
- if (facts.expandedSetCount > B65_MAX_GROUPING_SETS) {
9955
- throw new Error(
9956
- `ArgumentError: B65 expanded grouping set count ${facts.expandedSetCount} exceeds limit ${B65_MAX_GROUPING_SETS} (reason=GROUPING_SET_LIMIT_EXCEEDED).`
9957
- );
9958
- }
9959
- if (facts.canonicalItemCount > B65_MAX_GROUPING_ITEMS) {
9960
- throw new Error(
9961
- `ArgumentError: B65 canonical grouping item count ${facts.canonicalItemCount} exceeds limit ${B65_MAX_GROUPING_ITEMS} (reason=GROUPING_ITEM_LIMIT_EXCEEDED).`
9962
- );
9963
- }
9964
- };
9965
- function displayField(field) {
9966
- return field.tableAlias ? `${field.tableAlias}.${field.field}` : field.field;
9967
- }
9968
- function refFromName(name) {
9969
- const dot = name.indexOf(".");
9970
- return dot > 0 ? { type: "FIELD", tableAlias: name.slice(0, dot), field: name.slice(dot + 1) } : { type: "FIELD", tableAlias: null, field: name };
9971
- }
9972
- function collectGroupingRefs(node, out) {
9973
- if (node === null || typeof node !== "object") return;
9974
- if (Array.isArray(node)) {
9975
- node.forEach((item) => collectGroupingRefs(item, out));
9976
- return;
9977
- }
9978
- const value = node;
9979
- if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return;
9980
- if (value["type"] === "GROUPING_REF") {
9981
- out.push(value);
9982
- return;
9983
- }
9984
- Object.values(value).forEach((item) => collectGroupingRefs(item, out));
9985
- }
9986
- function collectNonAggregateFieldRefs(node, out) {
9987
- if (node === null || typeof node !== "object") return;
9988
- if (Array.isArray(node)) {
9989
- node.forEach((item) => collectNonAggregateFieldRefs(item, out));
9990
- return;
9991
- }
9992
- const value = node;
9993
- const type = value["type"];
9994
- if (type === "SELECT" || type === "SCALAR_SUBQUERY" || type === "GROUPING_REF" || type === "AGG_REF" || type === "AGG_ARITH") return;
9995
- if (type === "FIELD" && typeof value["field"] === "string") {
9996
- out.push({
9997
- type: "FIELD",
9998
- tableAlias: typeof value["tableAlias"] === "string" ? value["tableAlias"] : null,
9999
- field: value["field"]
10000
- });
10001
- return;
10002
- }
10003
- if (type === "FIELD_REF" && typeof value["field"] === "string") {
10004
- out.push(refFromName(value["field"]));
10005
- return;
10006
- }
10007
- Object.values(value).forEach((item) => collectNonAggregateFieldRefs(item, out));
10008
- }
10009
- function containsAggregate(node) {
10010
- if (node === null || typeof node !== "object") return false;
10011
- if (Array.isArray(node)) return node.some(containsAggregate);
10012
- const value = node;
10013
- if (value["type"] === "AGG_REF" || value["type"] === "AGG_ARITH") return true;
10014
- if (value["type"] === "SELECT" || value["type"] === "SCALAR_SUBQUERY") return false;
10015
- return Object.values(value).some(containsAggregate);
10016
- }
10017
- function isAggregateMaterializedAlias(column) {
10018
- if (!("alias" in column) || column.alias === null) return false;
10019
- if (column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL") return true;
10020
- if (column.type === "STRFUNC_COL" || column.type === "SCALAR_VALUE_COL") {
10021
- return containsAggregate(column);
10022
- }
10023
- return false;
10024
- }
10025
- function outputAliases(columns) {
10026
- return new Set(columns.flatMap(
10027
- (column) => "alias" in column && typeof column.alias === "string" ? [column.alias] : []
10028
- ));
10029
- }
10030
- function isAggregateSyntheticReference(ref) {
10031
- return ref.tableAlias === null && /^(COUNT|SUM|AVG|MAX|MIN|GROUP_CONCAT|STDDEV_POP|STDDEV_SAMP|VAR_POP|VAR_SAMP|MEDIAN|MODE)\(/.test(ref.field);
10032
- }
10033
- function validateGroupingRefMembership(ref, resolve2, canonicalItems) {
10034
- const resolved = resolve2(ref.field);
10035
- if (!resolved.physical) {
10036
- throw new Error(`ArgumentError: B65 grouping reference ${displayField(ref.field)} must resolve to a physical APP field.`);
10037
- }
10038
- if (!canonicalItems.has(resolved.canonicalId)) {
10039
- throw new Error(
10040
- `ArgumentError: B65 GROUPING argument ${displayField(ref.field)} is not present in grouping allItems (reason=B65_GROUPING_ARG_NOT_ITEM).`
10041
- );
10042
- }
10043
- bindGroupingRefCanonicalId(ref, resolved.canonicalId);
10044
- }
10045
- function validateDependency(ref, resolve2, canonicalItems, context) {
10046
- const resolved = resolve2(ref);
10047
- if (!resolved.physical || !canonicalItems.has(resolved.canonicalId)) {
10048
- throw new Error(
10049
- `ArgumentError: B65 non-aggregate field ${displayField(ref)} in ${context} is not a grouping item (reason=B65_NON_GROUPED_DEPENDENCY).`
10050
- );
10051
- }
10052
- }
10053
- function keyDependencies(key) {
10054
- if (key.type === "GROUPING_KEY") return [];
10055
- if (key.type === "FIELD_NAME") return [refFromName(key.name)];
10056
- const refs = [];
10057
- collectNonAggregateFieldRefs(key, refs);
10058
- return refs;
10059
- }
10060
- function validateGroupingPlanning(stmt, resolve2, planningGuardHook = () => void 0) {
10061
- const normalized = normalizeGroupingSpec(stmt);
10062
- const groupingRefs = [];
10063
- for (const column of stmt.columns) {
10064
- if (column.type !== "WINDOW_COL") collectGroupingRefs(column, groupingRefs);
10065
- }
10066
- collectGroupingRefs(stmt.orderBy, groupingRefs);
10067
- const forbiddenGroupingRefs = [];
10068
- collectGroupingRefs(stmt.where, forbiddenGroupingRefs);
10069
- collectGroupingRefs(stmt.having, forbiddenGroupingRefs);
10070
- for (const column of stmt.columns) {
10071
- if (column.type === "WINDOW_COL") collectGroupingRefs(column, forbiddenGroupingRefs);
10072
- }
10073
- if (forbiddenGroupingRefs.length > 0) {
10074
- throw new Error(
10075
- "ArgumentError: B65 GROUPING() is not allowed in WHERE, HAVING, JOIN, window, aggregate arguments, or DML expressions in Phase1."
10076
- );
10077
- }
10078
- if (normalized.type !== "GROUPING_SETS") {
10079
- if (groupingRefs.length > 0) {
10080
- throw new Error("ArgumentError: B65 GROUPING() requires GROUP BY ROLLUP or GROUPING SETS.");
10081
- }
10082
- return null;
10083
- }
10084
- if (stmt.distinct) {
10085
- throw new Error("ArgumentError: B65 SELECT DISTINCT is not supported in Phase1.");
10086
- }
10087
- if (stmt.orderMode === "KINTONE_NATIVE") {
10088
- throw new Error("ArgumentError: B65 KORDER BY is not supported in Phase1.");
10089
- }
10090
- if (stmt.columns.some((column) => column.type === "WINDOW_COL")) {
10091
- throw new Error("ArgumentError: B65 window functions are not supported in Phase1.");
10092
- }
10093
- if (stmt.columns.some(
10094
- (column) => column.type === "WILDCARD" || column.type === "PARENT_WILDCARD"
10095
- )) {
10096
- throw new Error("ArgumentError: B65 wildcard projection is not supported in Phase1.");
10097
- }
10098
- const resolvedSpec = resolveGroupingSpec(stmt, resolve2);
10099
- const canonicalItems = /* @__PURE__ */ new Set();
10100
- const resolvedItems = [];
10101
- for (const item of resolvedSpec.allItems) {
10102
- const resolved = item;
10103
- if (!resolved.physical) {
10104
- throw new Error(`ArgumentError: B65 grouping item ${displayField(item.field)} must resolve to a physical APP field.`);
10105
- }
10106
- if (!canonicalItems.has(resolved.canonicalId)) {
10107
- canonicalItems.add(resolved.canonicalId);
10108
- resolvedItems.push(resolved);
10109
- }
10110
- }
10111
- planningGuardHook({
10112
- expandedSetCount: normalized.sets.length,
10113
- canonicalItemCount: canonicalItems.size
10114
- });
10115
- for (const ref of groupingRefs) {
10116
- validateGroupingRefMembership(ref, resolve2, canonicalItems);
10117
- }
10118
- const aliases = outputAliases(stmt.columns);
10119
- for (const column of stmt.columns) {
10120
- if (column.type === "GROUPING_COL" || column.type === "AGGREGATE" || column.type === "ARITH_AGG_COL" || column.type === "LITERAL_COL" || column.type === "VARIABLE_COL" || column.type === "SCALAR_SUBQUERY_COL") continue;
10121
- const refs = [];
10122
- if (column.type === "FIELD") refs.push(refFromName(column.field));
10123
- else collectNonAggregateFieldRefs(column, refs);
10124
- for (const ref of refs) validateDependency(ref, resolve2, canonicalItems, "SELECT");
10125
- }
10126
- if (stmt.having) {
10127
- const refs = [];
10128
- collectNonAggregateFieldRefs(stmt.having, refs);
10129
- for (const ref of refs) {
10130
- if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
10131
- validateDependency(ref, resolve2, canonicalItems, "HAVING");
10132
- }
10133
- }
10134
- for (const order of stmt.orderBy) {
10135
- for (const ref of keyDependencies(order.key)) {
10136
- if (ref.tableAlias === null && aliases.has(ref.field) || isAggregateSyntheticReference(ref)) continue;
10137
- validateDependency(ref, resolve2, canonicalItems, "ORDER BY");
10138
- }
10139
- }
10140
- const collisionKeys = /* @__PURE__ */ new Set();
10141
- for (const item of resolvedItems) {
10142
- collisionKeys.add(item.directKey);
10143
- if (item.unqualifiedBridgeKey !== null) collisionKeys.add(item.unqualifiedBridgeKey);
10144
- }
10145
- for (const column of stmt.columns) {
10146
- if (!isAggregateMaterializedAlias(column)) continue;
10147
- const alias = "alias" in column ? column.alias : null;
10148
- if (alias === null) continue;
10149
- if (collisionKeys.has(alias)) {
10150
- throw new Error(
10151
- `ArgumentError: B65 aggregate alias ${alias} collides with a grouping runtime key (reason=B65_AGGREGATE_ALIAS_COLLISION).`
10152
- );
10153
- }
10154
- }
10155
- return resolvedSpec;
10156
- }
10157
-
10158
10246
  // src/api/fetchAll.ts
10159
10247
  async function fetchAll(fetcher, app, query, fields, options = {}) {
10160
10248
  const pageSize = options.pageSize ?? PAGE_SIZE_DEFAULT;
@@ -10932,9 +11020,13 @@ function applyHaving(rows, having, resolveFieldType, resolveFieldSemantics2) {
10932
11020
  if (having === null) return rows;
10933
11021
  return rows.filter((row) => evalWhere(having, row, resolveFieldType, void 0, resolveFieldSemantics2));
10934
11022
  }
10935
- function applyDistinct(rows, columns) {
11023
+ function applyDistinct(rows, columns, scalarCache, resolveFieldType, resolveFieldSemantics2) {
10936
11024
  if (rows.length === 0) return rows;
10937
- const keyFor = buildDistinctKeyBuilder(rows, columns);
11025
+ const keyFor = buildDistinctKeyBuilder(rows, columns, {
11026
+ scalarCache,
11027
+ resolveFieldType,
11028
+ resolveFieldSemantics: resolveFieldSemantics2
11029
+ });
10938
11030
  const seen = /* @__PURE__ */ new Set();
10939
11031
  return rows.filter((row) => {
10940
11032
  const key = keyFor(row);
@@ -10943,14 +11035,14 @@ function applyDistinct(rows, columns) {
10943
11035
  return true;
10944
11036
  });
10945
11037
  }
10946
- function buildDistinctKeyBuilder(rows, columns) {
11038
+ function buildDistinctKeyBuilder(rows, columns, context) {
11039
+ let sortedWildcardKeys = [];
10947
11040
  if (columns.some((c) => c.type === "WILDCARD")) {
10948
11041
  const allKeys = /* @__PURE__ */ new Set();
10949
11042
  for (const row of rows) {
10950
11043
  for (const k of Object.keys(row)) allKeys.add(k);
10951
11044
  }
10952
- const keys = [...allKeys].sort();
10953
- return (row) => JSON.stringify(keys.map((k) => row[k] !== void 0 ? row[k] : null));
11045
+ sortedWildcardKeys = [...allKeys].sort();
10954
11046
  }
10955
11047
  let sortedParentKeys = [];
10956
11048
  if (columns.some((c) => c.type === "PARENT_WILDCARD")) {
@@ -10962,25 +11054,12 @@ function buildDistinctKeyBuilder(rows, columns) {
10962
11054
  }
10963
11055
  sortedParentKeys = [...parentKeys].sort();
10964
11056
  }
10965
- return (row) => {
10966
- const values = [];
10967
- for (const col of columns) {
10968
- if (col.type === "FIELD") {
10969
- values.push(row[col.field] ?? "");
10970
- continue;
10971
- }
10972
- if (col.type === "WINDOW_COL") {
10973
- values.push(row[col.alias] ?? "");
10974
- continue;
10975
- }
10976
- if (col.type === "PARENT_WILDCARD") {
10977
- for (const k of sortedParentKeys) {
10978
- values.push(row[k] !== void 0 ? row[k] : null);
10979
- }
10980
- }
10981
- }
10982
- return JSON.stringify(values);
11057
+ const distinctContext = {
11058
+ ...context,
11059
+ wildcardKeys: sortedWildcardKeys,
11060
+ parentWildcardKeys: sortedParentKeys
10983
11061
  };
11062
+ return (row) => JSON.stringify(buildDistinctTuple(columns, row, distinctContext));
10984
11063
  }
10985
11064
  function applyOrderBy(rows, orderBy, optionOrders, sortKinds, fieldSemantics2, aliasEvaluator) {
10986
11065
  if (orderBy.length === 0) return rows;
@@ -11152,12 +11231,100 @@ function applyLimit(rows, limit, offset) {
11152
11231
  if (limit === null) return rows.slice(start);
11153
11232
  return rows.slice(start, start + limit);
11154
11233
  }
11234
+ function evaluateSelectColumnValue(column, row, columnIndex, context = {}) {
11235
+ switch (column.type) {
11236
+ case "VARIABLE_COL":
11237
+ throw new Error(`internal error: unresolved SELECT variable @${column.name}`);
11238
+ case "WILDCARD": {
11239
+ const keys = context.wildcardKeys ?? Object.keys(row);
11240
+ return {
11241
+ kind: "EXPANDED",
11242
+ entries: keys.map((key) => [key, row[key] !== void 0 ? row[key] : null])
11243
+ };
11244
+ }
11245
+ case "PARENT_WILDCARD": {
11246
+ const keys = context.parentWildcardKeys ?? Object.keys(row).filter((key) => key.startsWith("_p.")).sort();
11247
+ return {
11248
+ kind: "EXPANDED",
11249
+ entries: keys.map((key) => [key, row[key] !== void 0 ? row[key] : null])
11250
+ };
11251
+ }
11252
+ case "FIELD":
11253
+ return resolveFieldRef(row, column.field);
11254
+ case "LITERAL_COL":
11255
+ return column.value;
11256
+ case "AGGREGATE": {
11257
+ const source = aggregateSyntheticName(column.func, column.distinct, column.arg);
11258
+ return row[column.alias ?? source] ?? row[source] ?? "0";
11259
+ }
11260
+ case "ARITH_AGG_COL": {
11261
+ const source = column.alias ?? aggArithDefaultKey(column.expr);
11262
+ return row[source] ?? "0";
11263
+ }
11264
+ case "ARITH_COL":
11265
+ return String(evalArithExpr(column.expr, row));
11266
+ case "CASE_COL":
11267
+ return evalCaseWhen(
11268
+ column.expr,
11269
+ row,
11270
+ context.resolveFieldType,
11271
+ context.resolveFieldSemantics
11272
+ );
11273
+ case "GROUPING_COL":
11274
+ return evalGroupingRef(column.ref, row);
11275
+ case "STRFUNC_COL": {
11276
+ const source = stringFuncDefaultKey(column.expr);
11277
+ return hasAggregateInStringFuncExpr2(column.expr) ? row[column.alias ?? source] ?? row[source] ?? evalStringFunc(
11278
+ column.expr,
11279
+ row,
11280
+ context.resolveFieldType,
11281
+ context.resolveFieldSemantics
11282
+ ) : evalStringFunc(
11283
+ column.expr,
11284
+ row,
11285
+ context.resolveFieldType,
11286
+ context.resolveFieldSemantics
11287
+ );
11288
+ }
11289
+ case "SCALAR_VALUE_COL": {
11290
+ const source = scalarValueDefaultKey(column.expr);
11291
+ return scalarValueHasAggregate2(column.expr) ? row[column.alias ?? source] ?? row[source] ?? "" : String(evalScalarValueExpr(
11292
+ column.expr,
11293
+ row,
11294
+ context.resolveFieldType,
11295
+ context.resolveFieldSemantics
11296
+ ));
11297
+ }
11298
+ case "SCALAR_SUBQUERY_COL":
11299
+ return context.scalarCache?.get(columnIndex) ?? "";
11300
+ case "WINDOW_COL":
11301
+ return row[column.alias] ?? "";
11302
+ }
11303
+ }
11304
+ function buildDistinctTuple(columns, row, context = {}) {
11305
+ return columns.map((column, columnIndex) => {
11306
+ const value = evaluateSelectColumnValue(column, row, columnIndex, context);
11307
+ return typeof value === "string" ? value : value.entries.map(([, entryValue]) => entryValue);
11308
+ });
11309
+ }
11155
11310
  function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, resolveFieldSemantics2, hiddenQualifiedAliases) {
11156
11311
  if (columns.length === 1 && columns[0].type === "WILDCARD") {
11157
- const projected2 = rows.map((row) => stripHiddenQualifiedColumns(
11158
- stripParentShortcutColumns(row),
11159
- hiddenQualifiedAliases
11160
- ));
11312
+ const projected2 = rows.map((row) => {
11313
+ const visible = stripHiddenQualifiedColumns(
11314
+ stripParentShortcutColumns(row),
11315
+ hiddenQualifiedAliases
11316
+ );
11317
+ const value = evaluateSelectColumnValue(columns[0], row, 0, {
11318
+ wildcardKeys: Object.keys(visible)
11319
+ });
11320
+ const out = {};
11321
+ if (typeof value !== "string") {
11322
+ for (const [key, entryValue] of value.entries) {
11323
+ if (entryValue !== null) out[key] = entryValue;
11324
+ }
11325
+ }
11326
+ return out;
11327
+ });
11161
11328
  const cols = projected2.length > 0 ? Object.keys(projected2[0]) : [...sourceColumns ?? []];
11162
11329
  return { rows: projected2, columns: cols };
11163
11330
  }
@@ -11172,95 +11339,101 @@ function project(rows, columns, scalarCache, resolveFieldType, sourceColumns, re
11172
11339
  }
11173
11340
  const projected = rows.map((row, rowIdx) => {
11174
11341
  const out = {};
11342
+ const evaluationContext = {
11343
+ scalarCache,
11344
+ resolveFieldType,
11345
+ resolveFieldSemantics: resolveFieldSemantics2,
11346
+ wildcardKeys: Object.keys(stripHiddenQualifiedColumns(
11347
+ stripParentShortcutColumns(row),
11348
+ hiddenQualifiedAliases
11349
+ )),
11350
+ parentWildcardKeys: Object.keys(row).filter((key) => key.startsWith("_p.")).sort()
11351
+ };
11175
11352
  for (const [colIdx, col] of columns.entries()) {
11353
+ const value = evaluateSelectColumnValue(col, row, colIdx, evaluationContext);
11176
11354
  switch (col.type) {
11177
11355
  case "VARIABLE_COL":
11178
- throw new Error(`internal error: unresolved SELECT variable @${col.name}`);
11356
+ break;
11179
11357
  case "WILDCARD":
11180
- Object.assign(out, stripHiddenQualifiedColumns(
11181
- stripParentShortcutColumns(row),
11182
- hiddenQualifiedAliases
11183
- ));
11358
+ if (typeof value !== "string") {
11359
+ for (const [key, entryValue] of value.entries) {
11360
+ if (entryValue !== null) out[key] = entryValue;
11361
+ }
11362
+ }
11184
11363
  break;
11185
11364
  case "PARENT_WILDCARD": {
11186
- const parentKeys = Object.keys(row).filter((k) => k.startsWith("_p.")).sort();
11187
- for (const key of parentKeys) {
11188
- out[key] = row[key] ?? "";
11189
- if (rowIdx === 0) orderedKeys.push(key);
11365
+ if (typeof value !== "string") {
11366
+ for (const [key, entryValue] of value.entries) {
11367
+ if (entryValue !== null) out[key] = entryValue;
11368
+ if (rowIdx === 0) orderedKeys.push(key);
11369
+ }
11190
11370
  }
11191
11371
  break;
11192
11372
  }
11193
11373
  case "FIELD": {
11194
11374
  const key = outputKeys?.[colIdx] ?? col.alias ?? defaultFieldKeys.get(colIdx) ?? col.field;
11195
- out[key] = resolveFieldRef(row, col.field);
11375
+ out[key] = value;
11196
11376
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11197
11377
  break;
11198
11378
  }
11199
11379
  case "LITERAL_COL": {
11200
11380
  const key = outputKeys?.[colIdx] ?? col.alias ?? `'${col.value}'`;
11201
- out[key] = col.value;
11381
+ out[key] = value;
11202
11382
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11203
11383
  break;
11204
11384
  }
11205
11385
  case "AGGREGATE": {
11206
11386
  const srcKey = aggregateSyntheticName(col.func, col.distinct, col.arg);
11207
11387
  const dstKey = outputKeys?.[colIdx] ?? col.alias ?? srcKey;
11208
- out[dstKey] = row[col.alias ?? srcKey] ?? row[srcKey] ?? "0";
11388
+ out[dstKey] = value;
11209
11389
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(dstKey);
11210
11390
  break;
11211
11391
  }
11212
11392
  case "ARITH_AGG_COL": {
11213
11393
  const key = outputKeys?.[colIdx] ?? col.alias ?? aggArithDefaultKey(col.expr);
11214
- out[key] = row[key] ?? "0";
11394
+ out[key] = value;
11215
11395
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11216
11396
  break;
11217
11397
  }
11218
11398
  case "ARITH_COL": {
11219
- const val = evalArithExpr(col.expr, row);
11220
11399
  const key = outputKeys?.[colIdx] ?? col.alias ?? arithColDefaultKey(col.expr);
11221
- out[key] = String(val);
11400
+ out[key] = value;
11222
11401
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11223
11402
  break;
11224
11403
  }
11225
11404
  case "CASE_COL": {
11226
11405
  const key = outputKeys?.[colIdx] ?? col.alias ?? "case";
11227
- out[key] = evalCaseWhen(col.expr, row, resolveFieldType, resolveFieldSemantics2);
11406
+ out[key] = value;
11228
11407
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11229
11408
  break;
11230
11409
  }
11231
11410
  case "GROUPING_COL": {
11232
11411
  const key = outputKeys?.[colIdx] ?? col.alias ?? `GROUPING(${col.ref.field.tableAlias ? `${col.ref.field.tableAlias}.` : ""}${col.ref.field.field})`;
11233
- out[key] = evalGroupingRef(col.ref, row);
11412
+ out[key] = value;
11234
11413
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11235
11414
  break;
11236
11415
  }
11237
11416
  case "STRFUNC_COL": {
11238
11417
  const key = outputKeys?.[colIdx] ?? col.alias ?? stringFuncDefaultKey(col.expr);
11239
- if (hasAggregateInStringFuncExpr2(col.expr)) {
11240
- const srcKey = stringFuncDefaultKey(col.expr);
11241
- out[key] = row[col.alias ?? srcKey] ?? row[srcKey] ?? evalStringFunc(col.expr, row, resolveFieldType, resolveFieldSemantics2);
11242
- } else {
11243
- out[key] = evalStringFunc(col.expr, row, resolveFieldType, resolveFieldSemantics2);
11244
- }
11418
+ out[key] = value;
11245
11419
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11246
11420
  break;
11247
11421
  }
11248
11422
  case "SCALAR_VALUE_COL": {
11249
11423
  const key = outputKeys?.[colIdx] ?? col.alias ?? scalarValueDefaultKey(col.expr);
11250
- const srcKey = scalarValueDefaultKey(col.expr);
11251
- out[key] = scalarValueHasAggregate2(col.expr) ? row[col.alias ?? srcKey] ?? row[srcKey] ?? "" : String(evalScalarValueExpr(col.expr, row, resolveFieldType, resolveFieldSemantics2));
11424
+ out[key] = value;
11252
11425
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11253
11426
  break;
11254
11427
  }
11255
11428
  case "SCALAR_SUBQUERY_COL": {
11256
11429
  const key = outputKeys?.[colIdx] ?? col.alias ?? "(subquery)";
11257
- out[key] = scalarCache?.get(colIdx) ?? "";
11430
+ out[key] = value;
11258
11431
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11259
11432
  break;
11260
11433
  }
11261
11434
  case "WINDOW_COL": {
11262
11435
  const key = outputKeys?.[colIdx] ?? col.alias;
11263
- out[key] = row[col.alias] ?? "";
11436
+ out[key] = value;
11264
11437
  if (outputKeys === null && rowIdx === 0) orderedKeys.push(key);
11265
11438
  break;
11266
11439
  }
@@ -11551,7 +11724,13 @@ function runFullScan(input) {
11551
11724
  rows = applyHaving(rows, stmt.having, havingFieldTypeResolver, havingFieldSemanticsResolver);
11552
11725
  rows = applyWindow(rows, stmt.columns, optionOrders, sortKinds, effectiveOrderSemantics);
11553
11726
  if (stmt.distinct) {
11554
- rows = applyDistinct(rows, stmt.columns);
11727
+ rows = applyDistinct(
11728
+ rows,
11729
+ stmt.columns,
11730
+ scalarCache,
11731
+ fieldTypeResolver,
11732
+ fieldSemanticsResolver
11733
+ );
11555
11734
  }
11556
11735
  rows = applyOrderBy(
11557
11736
  rows,
@@ -19291,7 +19470,9 @@ function collectFullScanReasons(stmt) {
19291
19470
  if (grouping.type === "PLAIN")
19292
19471
  r.push("GROUP BY \u3042\u308A");
19293
19472
  else if (grouping.type === "GROUPING_SETS")
19294
- r.push(grouping.source === "ROLLUP" ? "ROLLUP \u3042\u308A" : "GROUPING SETS \u3042\u308A");
19473
+ r.push(
19474
+ grouping.source === "ROLLUP" ? "ROLLUP \u3042\u308A" : grouping.source === "CUBE" ? "CUBE \u3042\u308A" : "GROUPING SETS \u3042\u308A"
19475
+ );
19295
19476
  if (stmt.distinct)
19296
19477
  r.push("DISTINCT \u3042\u308A");
19297
19478
  if (stmt.columns.some((c) => c.type === "AGGREGATE" || c.type === "ARITH_AGG_COL"))