Skip to content

Disable decorrelation for count() with no grouping columns. - #412

Draft
Alena0704 wants to merge 2 commits into
OPENGPDB_STABLEfrom
orca-disable-count-decorrelation
Draft

Disable decorrelation for count() with no grouping columns.#412
Alena0704 wants to merge 2 commits into
OPENGPDB_STABLEfrom
orca-disable-count-decorrelation

Conversation

@Alena0704

Copy link
Copy Markdown
Contributor

Disable decorrelation for count() with no grouping columns.

In SQL standard, GROUP BY clause can represent two distinct operations: normal GROUP BY with grouping columns, and scalar GROUP BY without grouping columns. Their main difference is that the scalar GROUP BY always outputs exactly one row, even when input relation is empty. This especially matters for COUNT(*) and COUNT(attr) aggregates since in empty input their output is 0, not NULL.

During subquery decorrelation in ORCA, when pulling predicates through GpAgg, new grouping columns are added to it. This is fine for normal GROUP BYs, but for scalar GROUP BY (the case when there were no grouping columns originally), it changes behavior on empty input relations, which produces invalid output. This seems to be a well-known bug in existing database literature, known as the "COUNT bug".

This behavior was noticed previously in ORCA, and a "COALESCE fix" was added, converting NULLs back to 0. However, this fix was added in a previous transformation (CSubqueryHandler), so it was unnecessary in some cases, and also didn't cover all of them. Fixing this properly will require a partial rewrite of CDecorrelator to use better decorrelation algorithms that don't miss these edge cases.

As a temporary solution, this patch disables decorrelation for GpAgg with no grouping columns, if COUNT(*) or COUNT(attr) is present, as well as the COALESCE fix. This unfortunately results in less optimal plans in some cases, but distinguishing correct decorrelation from incorrect ones is complicated and requires big rewrites.

Tests affected by this:

  1. Search space size is reduced in 13 minidump tests, plans themselves weren't affected.
  2. Unnecessary COALESCE is removed from 7 minidump tests.
  3. Swap joins in InferPredicatesFromMultiSubquery.mdp, without affecting performance.
  4. Fix ScalarCorrelatedSubqueryCountStar.mdp and ScalarSubqueryCountStarInJoin.mdp, since previously they were fixing incorrect behavior.
  5. Change NullIf-With-Subquery.mdp and UnnestSQJoins.mdp to correlated versions (COALESCE fix worked for them before, so they were correct).
  6. Change plans of several regression tests in subselect.sql, subselect_gp.sql, subselect_gp_indexes.sql and eagerfree.sql, replacing them with correlated plans. Unfortunately, they were the ones where decorrelation was safe even without COALESCE fix, but there is no easy way to determine that with current architecture (COALESCE fix was applied to them previously regardless).

Ported from greengage #1658.

Co-Authored-By: Maxim Michkov m.michkov@arenadata.io

@Alena0704
Alena0704 force-pushed the orca-disable-count-decorrelation branch from eca564a to a504289 Compare August 18, 2026 20:35
@Alena0704
Alena0704 marked this pull request as draft August 18, 2026 20:39
@Alena0704

Alena0704 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

to reproduce the case

postgres=# create table o(a int);
NOTICE:  Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Greenplum Database data distribution key for this table.
HINT:  The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew.
ERROR:  relation "o" already exists
postgres=# insert into o values (0);
INSERT 0 1
postgres=# 
postgres=# set optimizer = on;
SET
postgres=# select * from o where a in (select count(*) from e where e.b = o.a);
 a 
---
(0 rows)

postgres=# set optimizer = off;
SET
postgres=# select * from o where a in (select count(*) from e where e.b = o.a);
 a 
---
 0
 0
(2 rows)

@Alena0704
Alena0704 force-pushed the orca-disable-count-decorrelation branch 2 times, most recently from 0106f13 to 2dced8f Compare August 19, 2026 06:09
Alena0704 and others added 2 commits August 19, 2026 13:46
In SQL standard, GROUP BY clause can represent two distinct operations: normal
GROUP BY with grouping columns, and scalar GROUP BY without grouping columns.
Their main difference is that the scalar GROUP BY always outputs exactly one
row, even when input relation is empty. This especially matters for COUNT(*)
and COUNT(attr) aggregates since in empty input their output is 0, not NULL.

During subquery decorrelation in ORCA, when pulling predicates through GpAgg,
new grouping columns are added to it. This is fine for normal GROUP BYs, but
for scalar GROUP BY (the case when there were no grouping columns originally),
it changes behavior on empty input relations, which produces invalid output.
This seems to be a well-known bug in existing database literature, known as
the "COUNT bug".

This behavior was noticed previously in ORCA, and a "COALESCE fix" was added,
converting NULLs back to 0. However, this fix was added in a previous
transformation (CSubqueryHandler), so it was unnecessary in some cases, and
also didn't cover all of them. Fixing this properly will require a partial
rewrite of CDecorrelator to use better decorrelation algorithms that don't
miss these edge cases.

As a temporary solution, this patch disables decorrelation for GpAgg with no
grouping columns, if COUNT(*) or COUNT(attr) is present, as well as the
COALESCE fix. This unfortunately results in less optimal plans in some cases,
but distinguishing correct decorrelation from incorrect ones is complicated
and requires big rewrites.

Tests affected by this:
1. Search space size is reduced in 13 minidump tests, plans themselves weren't
affected.
2. Unnecessary COALESCE is removed from 7 minidump tests.
3. Swap joins in InferPredicatesFromMultiSubquery.mdp, without affecting
performance.
4. Fix ScalarCorrelatedSubqueryCountStar.mdp and
ScalarSubqueryCountStarInJoin.mdp, since previously they were fixing incorrect
behavior.
5. Change NullIf-With-Subquery.mdp and UnnestSQJoins.mdp to correlated
versions (COALESCE fix worked for them before, so they were correct).
6. Change plans of several regression tests in subselect.sql,
subselect_gp.sql, subselect_gp_indexes.sql and eagerfree.sql, replacing them
with correlated plans. Unfortunately, they were the ones where decorrelation
was safe even without COALESCE fix, but there is no easy way to determine that
with current architecture (COALESCE fix was applied to them previously
regardless).

Ported from greengage #1658.

Co-Authored-By: Maxim Michkov <m.michkov@arenadata.io>
@Alena0704
Alena0704 force-pushed the orca-disable-count-decorrelation branch from 2dced8f to 11fc197 Compare August 19, 2026 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant