001package gudusoft.gsqlparser.ir.semantic; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.List; 006import java.util.Objects; 007 008/** 009 * Structured view of a single top-level {@code GROUP BY} element (slice 010 * 128). Where {@link StatementGraph#getGroupByColumnRefs()} flattens every 011 * grouping column into one ordered list (slice 127), this preserves the 012 * grouping <i>structure</i> the flat list discards: whether each top-level 013 * grouping element is plain, {@code ROLLUP(...)}, {@code CUBE(...)}, or 014 * {@code GROUPING SETS(...)}, together with that element's member columns. 015 * 016 * <p>One {@code GroupingElement} is emitted per top-level grouping item in 017 * document order. {@link StatementGraph#getGroupingElements()} is therefore 018 * empty iff the statement has no {@code GROUP BY} items, and a plain 019 * {@code GROUP BY a, b} yields {@code [SIMPLE: a, b]} (one element per 020 * top-level item). 021 * 022 * <p><b>Boundary (slice 128).</b> {@code members} is the flattened, ordered 023 * set of leaf column refs of the element. For {@link Kind#GROUPING_SETS} the 024 * internal set boundaries are flattened — {@code GROUPING SETS((a),(c))} and 025 * {@code GROUPING SETS((a,c))} both surface as {@code [GROUPING_SETS: a, c]}. 026 * Distinguishing the individual grouping sets is left to a future slice. 027 * Member collection reuses the slice-127 + slice-93 source-table fallback 028 * path, so members carry the same EXACT-binding guarantees as 029 * {@code groupByColumnRefs}. 030 */ 031public final class GroupingElement { 032 033 /** The grouping operation of a top-level {@code GROUP BY} element. */ 034 public enum Kind { 035 /** A plain grouping expression or parenthesized expression list. */ 036 SIMPLE, 037 /** {@code ROLLUP(...)}. */ 038 ROLLUP, 039 /** {@code CUBE(...)}. */ 040 CUBE, 041 /** {@code GROUPING SETS(...)}. */ 042 GROUPING_SETS 043 } 044 045 private final Kind kind; 046 private final List<ColumnRef> members; 047 048 public GroupingElement(Kind kind, List<ColumnRef> members) { 049 if (kind == null) { 050 throw new IllegalArgumentException("kind must not be null"); 051 } 052 if (members == null) { 053 throw new IllegalArgumentException("members must not be null"); 054 } 055 this.kind = kind; 056 this.members = Collections.unmodifiableList(new ArrayList<>(members)); 057 } 058 059 public Kind getKind() { 060 return kind; 061 } 062 063 /** Flattened, ordered leaf column refs of this element (may be empty). */ 064 public List<ColumnRef> getMembers() { 065 return members; 066 } 067 068 @Override 069 public boolean equals(Object o) { 070 if (this == o) return true; 071 if (!(o instanceof GroupingElement)) return false; 072 GroupingElement other = (GroupingElement) o; 073 return kind == other.kind && members.equals(other.members); 074 } 075 076 @Override 077 public int hashCode() { 078 return Objects.hash(kind, members); 079 } 080 081 @Override 082 public String toString() { 083 return kind + ":" + members; 084 } 085}