You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@calcite.apache.org by "Alessandro Solimando (Jira)" <ji...@apache.org> on 2025/08/04 12:54:00 UTC

[jira] [Created] (CALCITE-7125) Impossible to get a plan with partial aggregate push-down via IntersectToDistinctRule

Alessandro Solimando created CALCITE-7125:
---------------------------------------------

             Summary: Impossible to get a plan with partial aggregate push-down via IntersectToDistinctRule
                 Key: CALCITE-7125
                 URL: https://issues.apache.org/jira/browse/CALCITE-7125
             Project: Calcite
          Issue Type: Bug
          Components: core
    Affects Versions: 1.40.0
            Reporter: Alessandro Solimando


CALCITE-6893 changed _IntersectToDistinctRule_ to rewrite "INTERSECT DISTINCT" queries using a tagging plus filtering strategy rather than pushing aggregates into the "UNION" inputs.

Rationale was to let other rules (_AGGREGATE_UNION_TRANSPOSE_ is mentioned in the ticket description) produce the plan with partial aggregation push-down, and let the CBO do the choice.

Consider the following query:
{noformat}
SELECT empno FROM emp
INTERSECT
SELECT empno FROM emp
{noformat}

_IntersectToDistinctRule_, as per CALCITE-6893, would rewrite it as follows:

{noformat}
LogicalProject(EMPNO=[$0])
  LogicalFilter(condition=[AND(>($1, 0), >($2, 0))])
    LogicalAggregate(group=[{0}], count_i0=[COUNT() FILTER (WHERE i = 0)], count_i1=[COUNT() FILTER (WHERE i = 1)])
      LogicalUnion(all=[true])
        LogicalProject(EMPNO=[$0], i=[0]) // "tag" i=0
          TableScan(emp)
        LogicalProject(EMPNO=[$0], i=[1]) // "tag" i=1
          TableScan(emp)
{noformat}

This structure introduces:
 - A tagging column "i" added via projection
 - Two Projects wrapping each input
 - A single Aggregate with COUNT() FILTER expressions over those tags
 - A final filter requiring count_iN > 0, for N in "1..#operands"

With this rewrite, the planner:
 - Can no longer push aggregates into the union inputs
 - Cannot apply rules like _AGGREGATE_UNION_TRANSPOSE_/_AggregateUnionTransposeRule_ or _AGGREGATE_PROJECT_MERGE_/_AggregateProjectMergeRule_
 - Fails to recover the earlier, sometimes more efficient plan shape:

{noformat}
LogicalAggregate(group=[{0}])
  LogicalUnion(all=[true])
    LogicalAggregate(group=[{0}])
      ...
    LogicalAggregate(group=[{0}])
      ...
{noformat}

This prevents early row reduction and downstream rule composition, even when all relevant transformation rules are enabled.

Prior to CALCITE-6893, the same query with the same planner configuration produced the optimized form above. That plan is no longer reachable with the current {_}IntersectToDistinctRule{_}, and no combination of core rules (including _AGGREGATE_UNION_TRANSPOSE_/_AggregateUnionTransposeRule_, _PROJECT_SET_OP_TRANSPOSE_/_ProjectSetOpTransposeRule_, etc.) can recover it.

Unless I am mistaken and someone can suggest further rules that can lead to the same plan as before, this seems like a regression.

My proposal would be to either revert the change, or to at least expose the "old" behavior via a configuration flag.

My understanding is that, the original rewrite proposed in CALCITE-6893 was supposed to rewritable (by means of _AGGREGATE_UNION_TRANSPOSE_/_AggregateUnionTransposeRule_ rule). The rewrite was incorrect and the currently implemented form has been suggested, but it hasn't been discussed if the "new" plan shape (post CALCITE-6893) could be transformed into the "old" plan shape (pre CALCITE-6893).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)