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]

Reply via email to