Class SetOperations

java.lang.Object
dev.frostlake.executor.SetOperations

public final class SetOperations extends Object
Stateless set-operation row math — UNION / INTERSECT / EXCEPT (ALL and distinct variants) and DISTINCT — extracted from QueryExecutor. Pure List<Row> transforms; dedup uses a hash key on the row's values (O(n) rather than O(n^2)).

Row keys are built through per-column SetOpColumnCoercion normalizers, because Snowflake unifies the column types of set-operation branches before comparing: a VARCHAR branch against a TIMESTAMP branch coerces to TIMESTAMP ('2025-09-29 14:49:57.461' matches the timestamp value), a VARCHAR branch against a NUMBER branch coerces to NUMBER, and NUMBER branches of different scales compare by value (1.0 matches 1). The coercion for a column engages only when its values actually mix families across the two inputs — two VARCHAR branches stay text ('01' remains distinct from '1') — and numeric normalization is exact BigDecimal, never a double, so 20-digit NUMBER(38,0) keys that differ only in their low digits are never merged.

  • Method Details

    • applyUnion

      public static List<Row> applyUnion(List<Row> leftRows, List<Row> rightRowsRaw, boolean all)
      Apply UNION operation. The distinct variant deduplicates via a hash set on normalized row keys, turning the former O(n^2) scan into O(n).
    • applyUnion

      public static List<Row> applyUnion(List<Row> leftRows, List<Row> rightRowsRaw, boolean all, List<ResultSetColumn> leadingColumns)
      Apply UNION, with the LEADING branch's column layout available so an empty leading branch still types the union. Snowflake unifies branch types from the branch's declared column TYPE, not from the rows it happens to produce, so SELECT ts FROM t WHERE FALSE UNION ALL SELECT '…' still converts the string branch to TIMESTAMP.
    • coerceStringBranches

      public static void coerceStringBranches(List<List<Row>> branchRows, List<List<ResultSetColumn>> branchColumns, List<ResultSetColumn> unified)
      Convert every branch's values toward the unified column types, in place in branchRows. Live: when a set operation mixes a VARCHAR branch with a numeric or date branch, the STRING side's values convert to the non-string side's type whichever side LEADS — 'x' ∪ 1 and 1 ∪ 'x' both fail "Numeric value 'x' is not recognized", an empty string fails the same way, and a date pairing fails "Date 'y' is not recognized" — and every branch's values take the unified NUMBER's scale ('5' ∪ 1::NUMBER(10,2) renders 5.00 and 1.00, '2.7'::VARCHAR(3) ∪ 1 renders 2.70000 and 1.00000 at the unified NUMBER(18,5)). Only columns where a string branch actually participates are touched, so all-numeric unions keep their existing values. Rows are REPLACED, never mutated — a branch's rows can alias storage.
    • coerceStringToLeadingType

      public static Object coerceStringToLeadingType(Object leading, String text)
      One later-branch STRING converted to the leading branch's runtime type, or null to leave it. Shared with the ordering conditionals (GREATEST / LEAST), which coerce a string operand toward the other family exactly as a set operation's arms do — same rule, same sentences.
    • applyIntersect

      public static List<Row> applyIntersect(List<Row> leftRows, List<Row> rightRowsRaw, boolean all)
      Apply INTERSECT operation. ALL keeps min(count_left, count_right) occurrences via a right-side count map; the distinct variant uses hash-set membership — both O(n) instead of O(n^2).
    • applyExcept

      public static List<Row> applyExcept(List<Row> leftRows, List<Row> rightRowsRaw, boolean all)
      Apply EXCEPT operation. ALL keeps count_left - count_right occurrences via a right-side count map; the distinct variant uses hash-set membership — both O(n) instead of O(n^2).
    • applyDistinct

      public static List<Row> applyDistinct(List<Row> rows)
      Apply DISTINCT to remove duplicate rows (first occurrence kept, order preserved) — O(n) via a hash set on normalized row keys.
    • applyDistinct

      public static List<Row> applyDistinct(List<Row> rows, CollationSpec[] collations)
      Apply DISTINCT where some columns compare under a collation: values that differ only below the collation's strength are ONE row, and the row reports the smallest of them by raw text.
      Parameters:
      rows - the rows to de-duplicate
      collations - the collation of each column, null entries for the columns with none; null for a projection where no column carries one
      Returns:
      the distinct rows, first occurrence kept and order preserved