This is an automated email from the ASF dual-hosted git repository.
tuhaihe pushed a commit to branch REL_2_STABLE
in repository https://gitbox.apache.org/repos/asf/cloudberry.git
The following commit(s) were added to refs/heads/REL_2_STABLE by this push:
new fea29b2c0ce fix(orca): avoid crash on ordered-set aggregate without
direct args
fea29b2c0ce is described below
commit fea29b2c0ce3c4533ac35c358f09ac5f20118bde
Author: Jianghua Yang <[email protected]>
AuthorDate: Mon Aug 24 10:23:32 2026 +0800
fix(orca): avoid crash on ordered-set aggregate without direct args
There is a crash when use mode() for ORCA.
```
$ psql
gpadmin=# create table test_mode (a int, b text);
CREATE TABLE
gpadmin=# explain select mode() within group (order by b) from test_mode
limit 10;
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
!> \q
```
Reason: CUtils::FHasOrderedAggToSplit() unconditionally dereferenced the
direct-args child of an ordered-set aggregate. That child exists but is
empty for mode(), the only built-in ordered-set aggregate with no direct
argument, so the access tripped a GPOS_ASSERT in debug builds and caused
a SIGSEGV in release builds during ORCA query preprocessing.
Guard the ordered aggregate child accesses and bail out early when the
args, direct-args, or order children are empty. This leaves mode() as a
regular ordered-set Aggref for ORCA to plan, instead of trying to split
it into the gp_percentile-specific rewrite.
Add sql regress cases covering mode().
---
.../gporca/libgpopt/include/gpopt/base/CUtils.h | 3 +-
src/backend/gporca/libgpopt/src/base/CUtils.cpp | 26 +++++++++---
src/test/regress/expected/percentile.out | 49 ++++++++++++++++++++++
src/test/regress/sql/percentile.sql | 9 ++++
4 files changed, 80 insertions(+), 7 deletions(-)
diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h
b/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h
index 0aded041e00..fce0ef4af52 100644
--- a/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h
+++ b/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h
@@ -973,7 +973,8 @@ public:
// return true if given expression contains window aggregate function
static BOOL FHasAggWindowFunc(CExpression *pexpr);
- // return true if given expression contains ordered aggregate function
+ // return true if given expression contains an ordered aggregate
function
+ // that should be split to an internal gp_percentile aggregate
static BOOL FHasOrderedAggToSplit(CExpression *pexpr);
// return true if the given expression is a cross join
diff --git a/src/backend/gporca/libgpopt/src/base/CUtils.cpp
b/src/backend/gporca/libgpopt/src/base/CUtils.cpp
index 4776c602fcd..edf0d2b8f79 100644
--- a/src/backend/gporca/libgpopt/src/base/CUtils.cpp
+++ b/src/backend/gporca/libgpopt/src/base/CUtils.cpp
@@ -4834,18 +4834,32 @@ CUtils::FHasAggWindowFunc(CExpression *pexpr)
}
-// returns true if expression contains ordered aggregate function
+// returns true if expression contains an ordered aggregate function that
+// should be split to an internal gp_percentile aggregate
BOOL
CUtils::FHasOrderedAggToSplit(CExpression *pexpr)
{
GPOS_ASSERT(nullptr != pexpr);
CScalarAggFunc *popScAggFunc = CScalarAggFunc::PopConvert(pexpr->Pop());
- return popScAggFunc->AggKind() == EaggfunckindOrderedSet &&
- (!FScalarConst((*(*pexpr)[1])[0]) ||
- !FIsConstArray((*(*pexpr)[1])[0])) &&
- (FScalarIdent((*(*pexpr)[0])[0]) ||
- CScalarIdent::FCastedScId((*(*pexpr)[0])[0]));
+ if (popScAggFunc->AggKind() != EaggfunckindOrderedSet)
+ {
+ return false;
+ }
+
+ if (pexpr->Arity() <= EaggfuncIndexOrder ||
+ (*pexpr)[EaggfuncIndexArgs]->Arity() == 0 ||
+ (*pexpr)[EaggfuncIndexDirectArgs]->Arity() == 0 ||
+ (*pexpr)[EaggfuncIndexOrder]->Arity() == 0)
+ {
+ return false;
+ }
+
+ CExpression *pexprArg = (*(*pexpr)[EaggfuncIndexArgs])[0];
+ CExpression *pexprDirectArg = (*(*pexpr)[EaggfuncIndexDirectArgs])[0];
+
+ return (!FScalarConst(pexprDirectArg) ||
!FIsConstArray(pexprDirectArg)) &&
+ (FScalarIdent(pexprArg) ||
CScalarIdent::FCastedScId(pexprArg));
}
BOOL
diff --git a/src/test/regress/expected/percentile.out
b/src/test/regress/expected/percentile.out
index 981507d01a3..c1f4ec69da1 100644
--- a/src/test/regress/expected/percentile.out
+++ b/src/test/regress/expected/percentile.out
@@ -74,6 +74,55 @@ select b, percentile_cont(0.5) within group (order by a),
10 | 100 | 100 | 100
(11 rows)
+-- mode() is the only ordered-set aggregate with no direct argument; ORCA must
+-- plan it as an ordered-set aggregate instead of crashing in preprocessing.
+select mode() within group (order by b) from perct;
+ mode
+------
+ 1
+(1 row)
+
+select b, mode() within group (order by a) from perct group by b order by b;
+ b | mode
+----+------
+ 0 | 1
+ 1 | 10
+ 2 | 20
+ 3 | 30
+ 4 | 40
+ 5 | 50
+ 6 | 60
+ 7 | 70
+ 8 | 80
+ 9 | 90
+ 10 | 100
+(11 rows)
+
+-- Mix mode() with a splittable percentile and a regular aggregate.
+select mode() within group (order by b),
+ percentile_cont(0.6) within group (order by a), count(*) from perct;
+ mode | percentile_cont | count
+------+-----------------+-------
+ 1 | 60.4 | 100
+(1 row)
+
+select b, mode() within group (order by a),
+ percentile_cont(0.5) within group (order by a), count(*) from perct
group by b order by b;
+ b | mode | percentile_cont | count
+----+------+-----------------+-------
+ 0 | 1 | 5 | 9
+ 1 | 10 | 14.5 | 10
+ 2 | 20 | 24.5 | 10
+ 3 | 30 | 34.5 | 10
+ 4 | 40 | 44.5 | 10
+ 5 | 50 | 54.5 | 10
+ 6 | 60 | 64.5 | 10
+ 7 | 70 | 74.5 | 10
+ 8 | 80 | 84.5 | 10
+ 9 | 90 | 94.5 | 10
+ 10 | 100 | 100 | 1
+(11 rows)
+
select percentile_cont(0.2) within group (order by a) from generate_series(1,
100)a;
percentile_cont
-----------------
diff --git a/src/test/regress/sql/percentile.sql
b/src/test/regress/sql/percentile.sql
index 480adf395b0..bc7c327e770 100644
--- a/src/test/regress/sql/percentile.sql
+++ b/src/test/regress/sql/percentile.sql
@@ -45,6 +45,15 @@ select percentile_cont(0.5) within group (order by a),
median(a), percentile_disc(0.5) within group(order by a) from perct;
select b, percentile_cont(0.5) within group (order by a),
median(a), percentile_disc(0.5) within group(order by a) from perct
group by b order by b;
+-- mode() is the only ordered-set aggregate with no direct argument; ORCA must
+-- plan it as an ordered-set aggregate instead of crashing in preprocessing.
+select mode() within group (order by b) from perct;
+select b, mode() within group (order by a) from perct group by b order by b;
+-- Mix mode() with a splittable percentile and a regular aggregate.
+select mode() within group (order by b),
+ percentile_cont(0.6) within group (order by a), count(*) from perct;
+select b, mode() within group (order by a),
+ percentile_cont(0.5) within group (order by a), count(*) from perct
group by b order by b;
select percentile_cont(0.2) within group (order by a) from generate_series(1,
100)a;
select a / 10, percentile_cont(0.2) within group (order by a) from
generate_series(1, 100)a
group by a / 10 order by a / 10;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]