This is an automated email from the ASF dual-hosted git repository.

jiaqizho pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git


The following commit(s) were added to refs/heads/main by this push:
     new a840049c146 ORCA: Introduce hash windows aggregation when use the 
vectorization exector
a840049c146 is described below

commit a840049c146c82338b92ca72b50255a06813d96a
Author: zhoujiaqi <[email protected]>
AuthorDate: Thu Jul 24 14:58:47 2025 +0800

    ORCA: Introduce hash windows aggregation when use the vectorization exector
    
    In this PR, ORCA now supports generating `WindowHashAgg` plans which already
    have implementation in the vectorization executor. However, the CBDB row 
executor
    currently lacks implementation for the WindowHashAgg operator. To prevent 
ORCA
    from generating this operator in the row executor, I've added an struct 
which named
    `OptimizerOptions` to control the plan for row executor or vectorization 
executor.
    (By the way, ORCA may later generate plans specifically for the 
vectorization executor).
    
    The `WindowAgg` operator implemention in the vectorization execution is:
    
    1. First, sorting the input rows by `ORDER BY` keys
    2. Then do the `PARTITION` by `PARTITION BY` keys
    3. Finally do the window function.
    
    Since step1 must be globally sorted, it cannot be parallelized in the 
vectorization executor.
    This results in poor performance of the `WindowAgg` operator.
    
    By contrast, `WindowHashAgg` employs a more efficient approach:
    
    1. First hashes input data into buckets based on `PARTITION BY` keys
    2. Then sorts data `within each bucket` according to `ORDER BY` keys
    3. Finally computes window functions on the sorted bucket data
    
    For the row engine, `WindowHashAgg` operators will not be generated.
    
    Also current commit introduces a new GUC named 
`optimizer_force_window_hash_agg`
    to force generate plans with `WindowHashAgg` (Don't used this GUC expect 
debug ORCA).
    
    Co-Author-By: zhangyue <[email protected]>
---
 contrib/pg_stat_statements/pg_stat_statements.c    |  14 +-
 gpcontrib/pg_hint_plan/pg_hint_plan.c              |  12 +-
 src/backend/cdb/cdbllize.c                         |   1 +
 src/backend/cdb/cdbplan.c                          |  19 +
 src/backend/cdb/cdbtargeteddispatch.c              |   1 +
 src/backend/gpopt/CGPOptimizer.cpp                 |   9 +-
 src/backend/gpopt/config/CConfigParamMapping.cpp   |  13 +-
 .../gpopt/translate/CTranslatorDXLToPlStmt.cpp     | 415 ++++++++++++++++-----
 src/backend/gpopt/utils/COptTasks.cpp              |   6 +-
 .../libgpdbcost/include/gpdbcost/CCostModelGPDB.h  |   6 +
 .../gporca/libgpdbcost/src/CCostModelGPDB.cpp      |  66 +++-
 .../include/gpopt/base/CDistributionSpec.h         |   3 +
 .../include/gpopt/base/CDistributionSpecHashed.h   |   3 +-
 .../libgpopt/include/gpopt/operators/COperator.h   |   1 +
 .../gpopt/operators/CPhysicalHashSequenceProject.h | 123 ++++++
 .../gpopt/operators/CPhysicalSequenceProject.h     |   2 +-
 .../include/gpopt/translate/CTranslatorExprToDXL.h |   2 +-
 .../gporca/libgpopt/include/gpopt/xforms/CXform.h  |   1 +
 .../xforms/CXformImplementHashSequenceProject.h    |  95 +++++
 .../gporca/libgpopt/include/gpopt/xforms/xforms.h  |   1 +
 .../libgpopt/src/base/CDistributionSpecHashed.cpp  |  14 +-
 .../src/operators/CLogicalSequenceProject.cpp      |   2 +
 .../src/operators/CPhysicalHashSequenceProject.cpp | 151 ++++++++
 .../src/operators/CPhysicalSequenceProject.cpp     |   5 +-
 src/backend/gporca/libgpopt/src/operators/Makefile |   1 +
 .../src/translate/CTranslatorExprToDXL.cpp         |  16 +-
 .../gporca/libgpopt/src/xforms/CXformFactory.cpp   |   1 +
 .../xforms/CXformImplementHashSequenceProject.cpp  | 112 ++++++
 src/backend/gporca/libgpopt/src/xforms/Makefile    |   1 +
 .../naucrates/dxl/operators/CDXLPhysicalWindow.h   |   7 +-
 .../include/naucrates/dxl/xml/dxltokens.h          |   1 +
 .../include/naucrates/traceflags/traceflags.h      |   3 +
 .../src/operators/CDXLPhysicalWindow.cpp           |  20 +-
 .../src/parser/CParseHandlerPhysicalWindow.cpp     |   2 +-
 .../gporca/libnaucrates/src/xml/dxltokens.cpp      |   1 +
 src/backend/nodes/copyfuncs.c                      |  40 ++
 src/backend/nodes/outfast.c                        |   3 +
 src/backend/nodes/outfuncs.c                       |  30 ++
 src/backend/nodes/print.c                          |   2 +
 src/backend/nodes/readfast.c                       |   3 +
 src/backend/nodes/readfuncs.c                      |  31 ++
 src/backend/optimizer/plan/orca.c                  |   6 +-
 src/backend/optimizer/plan/planner.c               |  13 +-
 src/backend/optimizer/util/walkers.c               |   8 +
 src/backend/utils/misc/guc_gp.c                    |  12 +
 src/backend/utils/resource_manager/memquota.c      |   1 +
 src/include/gpopt/CGPOptimizer.h                   |   7 +-
 src/include/gpopt/config/CConfigParamMapping.h     |   2 +-
 .../gpopt/translate/CTranslatorDXLToPlStmt.h       |   9 +-
 src/include/gpopt/utils/COptTasks.h                |  10 +-
 src/include/nodes/nodes.h                          |   1 +
 src/include/nodes/plannodes.h                      |  41 ++
 src/include/optimizer/orca.h                       |   3 +-
 src/include/optimizer/orcaopt.h                    |  41 ++
 src/include/optimizer/planner.h                    |   8 +-
 src/include/utils/guc.h                            |   1 +
 src/include/utils/unsync_guc_name.h                |   1 +
 src/test/modules/delay_execution/delay_execution.c |   6 +-
 src/test/regress/hooktest/hook_test.c              |   8 +-
 src/test/unit/mock/gpopt_mock.c                    |   3 +-
 60 files changed, 1264 insertions(+), 156 deletions(-)

diff --git a/contrib/pg_stat_statements/pg_stat_statements.c 
b/contrib/pg_stat_statements/pg_stat_statements.c
index 3cd6221d001..b125bcca694 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -313,7 +313,8 @@ static void pgss_post_parse_analyze(ParseState *pstate, 
Query *query,
 static PlannedStmt *pgss_planner(Query *parse,
                                                                 const char 
*query_string,
                                                                 int 
cursorOptions,
-                                                                ParamListInfo 
boundParams);
+                                                                ParamListInfo 
boundParams,
+                                                                
OptimizerOptions *optimizer_options);
 static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
 static void pgss_ExecutorRun(QueryDesc *queryDesc,
                                                         ScanDirection 
direction,
@@ -866,7 +867,8 @@ static PlannedStmt *
 pgss_planner(Query *parse,
                         const char *query_string,
                         int cursorOptions,
-                        ParamListInfo boundParams)
+                        ParamListInfo boundParams,
+                        OptimizerOptions *optimizer_options)
 {
        PlannedStmt *result;
 
@@ -908,10 +910,10 @@ pgss_planner(Query *parse,
                {
                        if (prev_planner_hook)
                                result = prev_planner_hook(parse, query_string, 
cursorOptions,
-                                                                               
   boundParams);
+                                                                               
   boundParams, optimizer_options);
                        else
                                result = standard_planner(parse, query_string, 
cursorOptions,
-                                                                               
  boundParams);
+                                                                               
  boundParams, optimizer_options);
                }
                PG_FINALLY();
                {
@@ -945,10 +947,10 @@ pgss_planner(Query *parse,
        {
                if (prev_planner_hook)
                        result = prev_planner_hook(parse, query_string, 
cursorOptions,
-                                                                          
boundParams);
+                                                                          
boundParams, optimizer_options);
                else
                        result = standard_planner(parse, query_string, 
cursorOptions,
-                                                                         
boundParams);
+                                                                         
boundParams, optimizer_options);
        }
 
        return result;
diff --git a/gpcontrib/pg_hint_plan/pg_hint_plan.c 
b/gpcontrib/pg_hint_plan/pg_hint_plan.c
index 033a9d01200..1d37677019c 100644
--- a/gpcontrib/pg_hint_plan/pg_hint_plan.c
+++ b/gpcontrib/pg_hint_plan/pg_hint_plan.c
@@ -428,7 +428,7 @@ pg_hint_plan_add_paths_to_joinrel(PlannerInfo *root,
 static void *external_plan_hint_hook(Query *parse);
 #endif
 static PlannedStmt *pg_hint_plan_planner(Query *parse, const char 
*query_string, int cursorOptions,
-                                                                               
 ParamListInfo boundParams);
+                                                                               
 ParamListInfo boundParams, OptimizerOptions *optimizer_options);
 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
                                                                                
        int levels_needed,
                                                                                
        List *initial_rels);
@@ -3118,7 +3118,7 @@ pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const 
char *queryString,
  */
 static PlannedStmt *
 pg_hint_plan_planner(Query *parse, const char *query_string,
-       int cursorOptions, ParamListInfo boundParams)
+       int cursorOptions, ParamListInfo boundParams, OptimizerOptions 
*optimizer_options)
 {
        int                             save_nestlevel;
        PlannedStmt        *result;
@@ -3236,9 +3236,9 @@ pg_hint_plan_planner(Query *parse, const char 
*query_string,
                }
 
                if (prev_planner)
-                       result = (*prev_planner) (parse, query_string, 
cursorOptions, boundParams);
+                       result = (*prev_planner) (parse, query_string, 
cursorOptions, boundParams, optimizer_options);
                else
-                       result = standard_planner(parse, query_string, 
cursorOptions, boundParams);
+                       result = standard_planner(parse, query_string, 
cursorOptions, boundParams, optimizer_options);
 
                current_hint_str = prev_hint_str;
                recurse_level--;
@@ -3298,9 +3298,9 @@ standard_planner_proc:
        }
        current_hint_state = NULL;
        if (prev_planner)
-               result =  (*prev_planner) (parse, query_string, cursorOptions, 
boundParams);
+               result =  (*prev_planner) (parse, query_string, cursorOptions, 
boundParams, optimizer_options);
        else
-               result = standard_planner(parse, query_string, cursorOptions, 
boundParams);
+               result = standard_planner(parse, query_string, cursorOptions, 
boundParams, optimizer_options);
 
        /* The upper-level planner still needs the current hint state */
        if (HintStateStack != NIL)
diff --git a/src/backend/cdb/cdbllize.c b/src/backend/cdb/cdbllize.c
index 070ed51b99c..a4037c36158 100644
--- a/src/backend/cdb/cdbllize.c
+++ b/src/backend/cdb/cdbllize.c
@@ -1506,6 +1506,7 @@ motion_sanity_walker(Node *node, sanity_result_t *result)
        {
                case T_Result:
                case T_WindowAgg:
+               case T_WindowHashAgg:
                case T_TableFunctionScan:
                case T_ShareInputScan:
                case T_Append:
diff --git a/src/backend/cdb/cdbplan.c b/src/backend/cdb/cdbplan.c
index 1979ad38f8b..f48d8448338 100644
--- a/src/backend/cdb/cdbplan.c
+++ b/src/backend/cdb/cdbplan.c
@@ -694,6 +694,25 @@ plan_tree_mutator(Node *node,
                                return (Node *) newwindow;
                        }
                        break;
+               case T_WindowHashAgg:
+                       {
+                               WindowHashAgg  *window = (WindowHashAgg *) node;
+                               WindowHashAgg  *newwindow;
+
+                               FLATCOPY(newwindow, window, WindowHashAgg);
+                               PLANMUTATE(newwindow, window);
+
+                               COPYARRAY(newwindow, window, partNumCols, 
partColIdx);
+                               COPYARRAY(newwindow, window, partNumCols, 
partOperators);
+
+                               COPYARRAY(newwindow, window, ordNumCols, 
ordColIdx);
+                               COPYARRAY(newwindow, window, ordNumCols, 
ordOperators);
+                               MUTATE(newwindow->startOffset, 
window->startOffset, Node *);
+                               MUTATE(newwindow->endOffset, window->endOffset, 
Node *);
+
+                               return (Node *) newwindow;
+                       }
+                       break;
 
                case T_Unique:
                        {
diff --git a/src/backend/cdb/cdbtargeteddispatch.c 
b/src/backend/cdb/cdbtargeteddispatch.c
index b297164e273..da6f40c5971 100644
--- a/src/backend/cdb/cdbtargeteddispatch.c
+++ b/src/backend/cdb/cdbtargeteddispatch.c
@@ -522,6 +522,7 @@ DirectDispatchUpdateContentIdsFromPlan(PlannerInfo *root, 
Plan *plan)
                        DisableTargetedDispatch(&dispatchInfo);
                        break;
                case T_WindowAgg:
+               case T_WindowHashAgg:
                case T_TableFunctionScan:
                case T_RecursiveUnion:
                        /* no change to dispatchInfo */
diff --git a/src/backend/gpopt/CGPOptimizer.cpp 
b/src/backend/gpopt/CGPOptimizer.cpp
index 2ab834173f1..11d08f8438a 100644
--- a/src/backend/gpopt/CGPOptimizer.cpp
+++ b/src/backend/gpopt/CGPOptimizer.cpp
@@ -45,7 +45,8 @@ PlannedStmt *
 CGPOptimizer::GPOPTOptimizedPlan(
        Query *query,
        bool *
-               had_unexpected_failure  // output : set to true if optimizer 
unexpectedly failed to produce plan
+               had_unexpected_failure, // output : set to true if optimizer 
unexpectedly failed to produce plan
+       OptimizerOptions *opts
 )
 {
        SOptContext gpopt_context;
@@ -55,7 +56,7 @@ CGPOptimizer::GPOPTOptimizedPlan(
 
        GPOS_TRY
        {
-               plStmt = COptTasks::GPOPTOptimizedPlan(query, &gpopt_context);
+               plStmt = COptTasks::GPOPTOptimizedPlan(query, &gpopt_context, 
opts);
                // clean up context
                gpopt_context.Free(gpopt_context.epinQuery, 
gpopt_context.epinPlStmt);
        }
@@ -199,9 +200,9 @@ CGPOptimizer::TerminateGPOPT()
 //---------------------------------------------------------------------------
 extern "C" {
 PlannedStmt *
-GPOPTOptimizedPlan(Query *query, bool *had_unexpected_failure)
+GPOPTOptimizedPlan(Query *query, bool *had_unexpected_failure, 
OptimizerOptions *opts)
 {
-       return CGPOptimizer::GPOPTOptimizedPlan(query, had_unexpected_failure);
+       return CGPOptimizer::GPOPTOptimizedPlan(query, had_unexpected_failure, 
opts);
 }
 }
 
diff --git a/src/backend/gpopt/config/CConfigParamMapping.cpp 
b/src/backend/gpopt/config/CConfigParamMapping.cpp
index 802cf38785b..fcf4889ff8f 100644
--- a/src/backend/gpopt/config/CConfigParamMapping.cpp
+++ b/src/backend/gpopt/config/CConfigParamMapping.cpp
@@ -326,6 +326,12 @@ CConfigParamMapping::SConfigMappingElem 
CConfigParamMapping::m_elements[] = {
         false,  // m_negate_param
         GPOS_WSZ_LIT(
                 "Disable the dynamic seq/bitmap/index scan in partition 
table")},
+
+       {EopttraceEnableWindowHashAgg, &optimizer_force_window_hash_agg,
+        false,  // m_negate_param
+        GPOS_WSZ_LIT(
+                "Enable create window hash agg")},
+       
 };
 
 //---------------------------------------------------------------------------
@@ -339,7 +345,8 @@ CConfigParamMapping::SConfigMappingElem 
CConfigParamMapping::m_elements[] = {
 CBitSet *
 CConfigParamMapping::PackConfigParamInBitset(
        CMemoryPool *mp,
-       ULONG xform_id  // number of available xforms
+       ULONG xform_id, // number of available xforms
+       BOOL create_vec_plan
 )
 {
        CBitSet *traceflag_bitset = GPOS_NEW(mp) CBitSet(mp, EopttraceSentinel);
@@ -561,6 +568,10 @@ CConfigParamMapping::PackConfigParamInBitset(
                        
GPOPT_DISABLE_XFORM_TF(CXform::ExfRightOuterJoin2HashJoin));
        }
 
+       if (create_vec_plan) {
+               traceflag_bitset->ExchangeSet(EopttraceEnableWindowHashAgg);
+       }
+
        return traceflag_bitset;
 }
 
diff --git a/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp 
b/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp
index c7b89c0384e..e706f5f68ac 100644
--- a/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp
+++ b/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp
@@ -407,8 +407,13 @@ CTranslatorDXLToPlStmt::TranslateDXLOperatorToPlan(
                }
                case EdxlopPhysicalWindow:
                {
-                       plan = TranslateDXLWindow(dxlnode, output_context,
+                       if 
(CDXLPhysicalWindow::Cast(dxlnode->GetOperator())->IsWindowHashAgg()) {
+                               plan = TranslateDXLWindowHashAgg(dxlnode, 
output_context,
+                                                                               
ctxt_translation_prev_siblings);
+                       } else {
+                               plan = TranslateDXLWindowAgg(dxlnode, 
output_context,
                                                                          
ctxt_translation_prev_siblings);
+                       }
                        break;
                }
                case EdxlopPhysicalSort:
@@ -3040,16 +3045,121 @@ CTranslatorDXLToPlStmt::TranslateDXLAgg(
        return (Plan *) agg;
 }
 
+static
+int WindowFrameSpecToOptions(const EdxlFrameSpec &dxlFS) {
+       int winFrameOptions = 0;
+       if (EdxlfsRow == dxlFS)
+       {
+               winFrameOptions |= FRAMEOPTION_ROWS;
+       }
+       else if (EdxlfsGroups == dxlFS)
+       {
+               winFrameOptions |= FRAMEOPTION_GROUPS;
+       }
+       else
+       {
+               winFrameOptions |= FRAMEOPTION_RANGE;
+       }
+       return winFrameOptions;
+}
+
+static
+int WindowFrameExclusionStrategyToOptions(const EdxlFrameExclusionStrategy 
&dxlFES) {
+       int winFrameOptions = 0;
+       if (dxlFES == EdxlfesCurrentRow)
+       {
+               winFrameOptions |= FRAMEOPTION_EXCLUDE_CURRENT_ROW;
+       }
+       else if (dxlFES == EdxlfesGroup)
+       {
+               winFrameOptions |= FRAMEOPTION_EXCLUDE_GROUP;
+       }
+       else if (dxlFES == EdxlfesTies)
+       {
+               winFrameOptions |= FRAMEOPTION_EXCLUDE_TIES;
+       }
+
+       return winFrameOptions;
+}
+
+static 
+int WindowFrameStartBoundaryToOptions(const EdxlFrameBoundary &dxlFB) {
+       int winFrameOptions = 0;
+       if (dxlFB == EdxlfbUnboundedPreceding)
+       {
+               winFrameOptions |= FRAMEOPTION_START_UNBOUNDED_PRECEDING;
+       }
+       if (dxlFB == EdxlfbBoundedPreceding)
+       {
+               winFrameOptions |= FRAMEOPTION_START_OFFSET_PRECEDING;
+       }
+       if (dxlFB == EdxlfbCurrentRow)
+       {
+               winFrameOptions |= FRAMEOPTION_START_CURRENT_ROW;
+       }
+       if (dxlFB == EdxlfbBoundedFollowing)
+       {
+               winFrameOptions |= FRAMEOPTION_START_OFFSET_FOLLOWING;
+       }
+       if (dxlFB == EdxlfbUnboundedFollowing)
+       {
+               winFrameOptions |= FRAMEOPTION_START_UNBOUNDED_FOLLOWING;
+       }
+       if (dxlFB == EdxlfbDelayedBoundedPreceding)
+       {
+               winFrameOptions |= FRAMEOPTION_START_OFFSET_PRECEDING;
+       }
+       if (dxlFB == EdxlfbDelayedBoundedFollowing)
+       {
+               winFrameOptions |= FRAMEOPTION_START_OFFSET_FOLLOWING;
+       }
+       return winFrameOptions;
+}
+
+static 
+int WindowFrameEndBoundaryToOptions(const EdxlFrameBoundary &dxlFB) {
+       int winFrameOptions = 0;
+       if (dxlFB == EdxlfbUnboundedPreceding)
+       {
+               winFrameOptions |= FRAMEOPTION_END_UNBOUNDED_PRECEDING;
+       }
+       if (dxlFB == EdxlfbBoundedPreceding)
+       {
+               winFrameOptions |= FRAMEOPTION_END_OFFSET_PRECEDING;
+       }
+       if (dxlFB == EdxlfbCurrentRow)
+       {
+               winFrameOptions |= FRAMEOPTION_END_CURRENT_ROW;
+       }
+       if (dxlFB == EdxlfbBoundedFollowing)
+       {
+               winFrameOptions |= FRAMEOPTION_END_OFFSET_FOLLOWING;
+       }
+       if (dxlFB == EdxlfbUnboundedFollowing)
+       {
+               winFrameOptions |= FRAMEOPTION_END_UNBOUNDED_FOLLOWING;
+       }
+       if (dxlFB == EdxlfbDelayedBoundedPreceding)
+       {
+               winFrameOptions |= FRAMEOPTION_END_OFFSET_PRECEDING;
+       }
+       if (dxlFB == EdxlfbDelayedBoundedFollowing)
+       {
+               winFrameOptions |= FRAMEOPTION_END_OFFSET_FOLLOWING;
+       }
+       return winFrameOptions;
+}
+
 //---------------------------------------------------------------------------
 //     @function:
-//             CTranslatorDXLToPlStmt::TranslateDXLWindow
+//             CTranslatorDXLToPlStmt::TranslateDXLWindowAgg
 //
 //     @doc:
 //             Translate DXL window node into GPDB window plan node
 //
 //---------------------------------------------------------------------------
 Plan *
-CTranslatorDXLToPlStmt::TranslateDXLWindow(
+CTranslatorDXLToPlStmt::TranslateDXLWindowAgg(
        const CDXLNode *window_dxlnode, CDXLTranslateContext *output_context,
        CDXLTranslationContextArray *ctxt_translation_prev_siblings)
 {
@@ -3104,9 +3214,6 @@ CTranslatorDXLToPlStmt::TranslateDXLWindow(
        const ULongPtrArray *part_by_cols_array =
                window_dxlop->GetPartByColsArray();
        window->partNumCols = part_by_cols_array->Size();
-       window->partColIdx = nullptr;
-       window->partOperators = nullptr;
-       window->partCollations = nullptr;
 
        if (window->partNumCols > 0)
        {
@@ -3116,6 +3223,10 @@ CTranslatorDXLToPlStmt::TranslateDXLWindow(
                        (Oid *) gpdb::GPDBAlloc(window->partNumCols * 
sizeof(Oid));
                window->partCollations =
                        (Oid *) gpdb::GPDBAlloc(window->partNumCols * 
sizeof(Oid));
+       } else {
+               window->partColIdx = nullptr;
+               window->partOperators = nullptr;
+               window->partCollations = nullptr;
        }
 
        const ULONG num_of_part_cols = part_by_cols_array->Size();
@@ -3196,34 +3307,214 @@ CTranslatorDXLToPlStmt::TranslateDXLWindow(
                if (nullptr != window_key->GetWindowFrame())
                {
                        window->frameOptions = FRAMEOPTION_NONDEFAULT;
-                       if (EdxlfsRow == window_frame->ParseDXLFrameSpec())
-                       {
-                               window->frameOptions |= FRAMEOPTION_ROWS;
-                       }
-                       else if (EdxlfsGroups == 
window_frame->ParseDXLFrameSpec())
-                       {
-                               window->frameOptions |= FRAMEOPTION_GROUPS;
-                       }
-                       else
-                       {
-                               window->frameOptions |= FRAMEOPTION_RANGE;
-                       }
+                       window->frameOptions |= 
WindowFrameSpecToOptions(window_frame->ParseDXLFrameSpec());
+                       window->frameOptions |= 
WindowFrameExclusionStrategyToOptions(
+                               window_frame->ParseFrameExclusionStrategy());
 
-                       if (window_frame->ParseFrameExclusionStrategy() ==
-                               EdxlfesCurrentRow)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_EXCLUDE_CURRENT_ROW;
-                       }
-                       else if (window_frame->ParseFrameExclusionStrategy() ==
-                                        EdxlfesGroup)
+                       // translate the CDXLNodes representing the leading and 
trailing edge
+                       CDXLTranslationContextArray *child_contexts =
+                               GPOS_NEW(m_mp) 
CDXLTranslationContextArray(m_mp);
+                       child_contexts->Append(&child_context);
+
+                       CMappingColIdVarPlStmt colid_var_mapping =
+                               CMappingColIdVarPlStmt(m_mp, nullptr, 
child_contexts,
+                                                                          
output_context, m_dxl_to_plstmt_context);
+
+                       // Translate lead boundary
+                       //
+                       // Note that we don't distinguish between the delayed 
and undelayed
+                       // versions beoynd this point. Executor will make that 
decision
+                       // without our help.
+                       //
+                       CDXLNode *win_frame_leading_dxlnode = 
window_frame->PdxlnLeading();
+                       window->frameOptions |= 
WindowFrameStartBoundaryToOptions(
+                                       
CDXLScalarWindowFrameEdge::Cast(win_frame_leading_dxlnode->GetOperator())
+                                       ->ParseDXLFrameBoundary());
+                       if (0 != win_frame_leading_dxlnode->Arity())
                        {
-                               window->frameOptions |= 
FRAMEOPTION_EXCLUDE_GROUP;
+                               window->startOffset =
+                                       (Node *) 
m_translator_dxl_to_scalar->TranslateDXLToScalar(
+                                               
(*win_frame_leading_dxlnode)[0], &colid_var_mapping);
                        }
-                       else if (window_frame->ParseFrameExclusionStrategy() == 
EdxlfesTies)
+
+                       // And the same for the trail boundary
+                       CDXLNode *win_frame_trailing_dxlnode =
+                               window_frame->PdxlnTrailing();
+                       window->frameOptions |= WindowFrameEndBoundaryToOptions(
+                               CDXLScalarWindowFrameEdge::Cast(
+                                       
win_frame_trailing_dxlnode->GetOperator())
+                                       ->ParseDXLFrameBoundary());
+
+                       if (0 != win_frame_trailing_dxlnode->Arity())
                        {
-                               window->frameOptions |= 
FRAMEOPTION_EXCLUDE_TIES;
+                               window->endOffset =
+                                       (Node *) 
m_translator_dxl_to_scalar->TranslateDXLToScalar(
+                                               
(*win_frame_trailing_dxlnode)[0], &colid_var_mapping);
                        }
 
+                       window->startInRangeFunc = 
window_frame->PdxlnStartInRangeFunc();
+                       window->endInRangeFunc = 
window_frame->PdxlnEndInRangeFunc();
+                       window->inRangeColl = window_frame->PdxlnInRangeColl();
+                       window->inRangeAsc = window_frame->PdxlnInRangeAsc();
+                       window->inRangeNullsFirst = 
window_frame->PdxlnInRangeNullsFirst();
+
+                       // cleanup
+                       child_contexts->Release();
+               }
+               else
+               {
+                       window->frameOptions = FRAMEOPTION_DEFAULTS;
+               }
+       }
+
+       SetParamIds(plan);
+
+       // cleanup
+       child_contexts->Release();
+
+       return (Plan *) window;
+}
+
+//---------------------------------------------------------------------------
+//     @function:
+//             CTranslatorDXLToPlStmt::TranslateDXLWindowHashAgg
+//
+//     @doc:
+//             Translate DXL window node into GPDB window hash plan node
+//
+//---------------------------------------------------------------------------
+Plan *
+CTranslatorDXLToPlStmt::TranslateDXLWindowHashAgg(
+       const CDXLNode *window_dxlnode, CDXLTranslateContext *output_context,
+       CDXLTranslationContextArray *ctxt_translation_prev_siblings)
+{
+       WindowHashAgg *window = MakeNode(WindowHashAgg);
+
+       Plan *plan = &(window->plan);
+       plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId();
+
+       CDXLPhysicalWindow *window_dxlop =
+               CDXLPhysicalWindow::Cast(window_dxlnode->GetOperator());
+
+       // translate the operator costs
+       TranslatePlanCosts(window_dxlnode, plan);
+
+       // translate children
+       CDXLNode *child_dxlnode = (*window_dxlnode)[EdxlwindowIndexChild];
+       CDXLNode *project_list_dxlnode = 
(*window_dxlnode)[EdxlwindowIndexProjList];
+       CDXLNode *filter_dxlnode = (*window_dxlnode)[EdxlwindowIndexFilter];
+
+       CDXLTranslateContext child_context(m_mp, true,
+                                                                          
output_context->GetColIdToParamIdMap());
+       Plan *child_plan = TranslateDXLOperatorToPlan(
+               child_dxlnode, &child_context, ctxt_translation_prev_siblings);
+
+       CDXLTranslationContextArray *child_contexts =
+               GPOS_NEW(m_mp) CDXLTranslationContextArray(m_mp);
+       child_contexts->Append(&child_context);
+
+       // translate proj list and filter
+       TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode,
+                                                          nullptr,      // 
translate context for the base table
+                                                          child_contexts,      
// pdxltrctxRight,
+                                                          &plan->targetlist, 
&plan->qual, output_context);
+
+       ListCell *lc;
+
+       foreach (lc, plan->targetlist)
+       {
+               TargetEntry *target_entry = (TargetEntry *) lfirst(lc);
+               if (IsA(target_entry->expr, WindowFunc))
+               {
+                       WindowFunc *window_func = (WindowFunc *) 
target_entry->expr;
+                       window->winref = window_func->winref;
+                       break;
+               }
+       }
+
+       plan->lefttree = child_plan;
+
+       // translate partition columns
+       const ULongPtrArray *part_by_cols_array =
+               window_dxlop->GetPartByColsArray();
+       window->partNumCols = part_by_cols_array->Size();
+
+       if (window->partNumCols > 0)
+       {
+               window->partColIdx = (AttrNumber *) gpdb::GPDBAlloc(
+                       window->partNumCols * sizeof(AttrNumber));
+               window->partOperators =
+                       (Oid *) gpdb::GPDBAlloc(window->partNumCols * 
sizeof(Oid));
+               window->partCollations =
+                       (Oid *) gpdb::GPDBAlloc(window->partNumCols * 
sizeof(Oid));
+       } else {
+               window->partColIdx = nullptr;
+               window->partOperators = nullptr;
+               window->partCollations = nullptr;
+       }
+
+       const ULONG num_of_part_cols = part_by_cols_array->Size();
+       for (ULONG ul = 0; ul < num_of_part_cols; ul++)
+       {
+               ULONG part_colid = *((*part_by_cols_array)[ul]);
+               const TargetEntry *te_part_colid =
+                       child_context.GetTargetEntry(part_colid);
+               if (nullptr == te_part_colid)
+               {
+                       GPOS_RAISE(gpdxl::ExmaDXL, 
gpdxl::ExmiDXL2PlStmtAttributeNotFound,
+                                          part_colid);
+               }
+               window->partColIdx[ul] = te_part_colid->resno;
+
+               // Also find the equality operators to use for each 
partitioning key col.
+               Oid type_id = gpdb::ExprType((Node *) te_part_colid->expr);
+               window->partOperators[ul] = gpdb::GetEqualityOp(type_id);
+               Assert(window->partOperators[ul] != 0);
+               window->partCollations[ul] =
+                       gpdb::ExprCollation((Node *) te_part_colid->expr);
+       }
+
+       // translate window keys
+       const ULONG size = window_dxlop->WindowKeysCount();
+       if (size > 1)
+       {
+               GpdbEreport(ERRCODE_INTERNAL_ERROR, ERROR,
+                                       "ORCA produced a plan with more than 
one window key",
+                                       nullptr);
+       }
+       GPOS_ASSERT(size <= 1 && "cannot have more than one window key");
+
+       if (size == 1)
+       {
+               // translate the sorting columns used in the window key
+               const CDXLWindowKey *window_key = 
window_dxlop->GetDXLWindowKeyAt(0);
+               const CDXLWindowFrame *window_frame = 
window_key->GetWindowFrame();
+               const CDXLNode *sort_col_list_dxlnode = 
window_key->GetSortColListDXL();
+
+               const ULONG num_of_cols = sort_col_list_dxlnode->Arity();
+
+               window->ordNumCols = num_of_cols;
+               window->ordColIdx =
+                       (AttrNumber *) gpdb::GPDBAlloc(num_of_cols * 
sizeof(AttrNumber));
+               window->ordOperators =
+                       (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid));
+               window->ordCollations =
+                       (Oid *) gpdb::GPDBAlloc(num_of_cols * sizeof(Oid));
+               window->ordNullsFirst =
+                       (bool *) gpdb::GPDBAlloc(num_of_cols * sizeof(bool));
+               // different with windowagg, sort should be full
+               TranslateSortCols(sort_col_list_dxlnode, &child_context,
+                                                 window->ordColIdx, 
window->ordOperators,
+                                                 window->ordCollations, 
window->ordNullsFirst);
+
+               // translate the window frame specified in the window key
+               if (nullptr != window_key->GetWindowFrame())
+               {
+                       window->frameOptions = FRAMEOPTION_NONDEFAULT;
+                       window->frameOptions |= 
WindowFrameSpecToOptions(window_frame->ParseDXLFrameSpec());
+                       window->frameOptions |= 
WindowFrameExclusionStrategyToOptions(
+                               window_frame->ParseFrameExclusionStrategy());
+
                        // translate the CDXLNodes representing the leading and 
trailing edge
                        CDXLTranslationContextArray *child_contexts =
                                GPOS_NEW(m_mp) 
CDXLTranslationContextArray(m_mp);
@@ -3240,38 +3531,9 @@ CTranslatorDXLToPlStmt::TranslateDXLWindow(
                        // without our help.
                        //
                        CDXLNode *win_frame_leading_dxlnode = 
window_frame->PdxlnLeading();
-                       EdxlFrameBoundary lead_boundary_type =
-                               CDXLScalarWindowFrameEdge::Cast(
-                                       
win_frame_leading_dxlnode->GetOperator())
-                                       ->ParseDXLFrameBoundary();
-                       if (lead_boundary_type == EdxlfbUnboundedPreceding)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_START_UNBOUNDED_PRECEDING;
-                       }
-                       if (lead_boundary_type == EdxlfbBoundedPreceding)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_START_OFFSET_PRECEDING;
-                       }
-                       if (lead_boundary_type == EdxlfbCurrentRow)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_START_CURRENT_ROW;
-                       }
-                       if (lead_boundary_type == EdxlfbBoundedFollowing)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_START_OFFSET_FOLLOWING;
-                       }
-                       if (lead_boundary_type == EdxlfbUnboundedFollowing)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_START_UNBOUNDED_FOLLOWING;
-                       }
-                       if (lead_boundary_type == EdxlfbDelayedBoundedPreceding)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_START_OFFSET_PRECEDING;
-                       }
-                       if (lead_boundary_type == EdxlfbDelayedBoundedFollowing)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_START_OFFSET_FOLLOWING;
-                       }
+                       window->frameOptions |= 
WindowFrameStartBoundaryToOptions(
+                                       
CDXLScalarWindowFrameEdge::Cast(win_frame_leading_dxlnode->GetOperator())
+                                       ->ParseDXLFrameBoundary());
                        if (0 != win_frame_leading_dxlnode->Arity())
                        {
                                window->startOffset =
@@ -3282,38 +3544,11 @@ CTranslatorDXLToPlStmt::TranslateDXLWindow(
                        // And the same for the trail boundary
                        CDXLNode *win_frame_trailing_dxlnode =
                                window_frame->PdxlnTrailing();
-                       EdxlFrameBoundary trail_boundary_type =
+                       window->frameOptions |= WindowFrameEndBoundaryToOptions(
                                CDXLScalarWindowFrameEdge::Cast(
                                        
win_frame_trailing_dxlnode->GetOperator())
-                                       ->ParseDXLFrameBoundary();
-                       if (trail_boundary_type == EdxlfbUnboundedPreceding)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_END_UNBOUNDED_PRECEDING;
-                       }
-                       if (trail_boundary_type == EdxlfbBoundedPreceding)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_END_OFFSET_PRECEDING;
-                       }
-                       if (trail_boundary_type == EdxlfbCurrentRow)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_END_CURRENT_ROW;
-                       }
-                       if (trail_boundary_type == EdxlfbBoundedFollowing)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_END_OFFSET_FOLLOWING;
-                       }
-                       if (trail_boundary_type == EdxlfbUnboundedFollowing)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_END_UNBOUNDED_FOLLOWING;
-                       }
-                       if (trail_boundary_type == 
EdxlfbDelayedBoundedPreceding)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_END_OFFSET_PRECEDING;
-                       }
-                       if (trail_boundary_type == 
EdxlfbDelayedBoundedFollowing)
-                       {
-                               window->frameOptions |= 
FRAMEOPTION_END_OFFSET_FOLLOWING;
-                       }
+                                       ->ParseDXLFrameBoundary());
+
                        if (0 != win_frame_trailing_dxlnode->Arity())
                        {
                                window->endOffset =
diff --git a/src/backend/gpopt/utils/COptTasks.cpp 
b/src/backend/gpopt/utils/COptTasks.cpp
index 0f5aadbd82a..dd0e61116d8 100644
--- a/src/backend/gpopt/utils/COptTasks.cpp
+++ b/src/backend/gpopt/utils/COptTasks.cpp
@@ -913,7 +913,7 @@ COptTasks::OptimizeTask(void *ptr)
        {
                // set trace flags
                trace_flags = CConfigParamMapping::PackConfigParamInBitset(
-                       mp, CXform::ExfSentinel);
+                       mp, CXform::ExfSentinel, opt_ctxt->m_create_vec_plan);
                SetTraceflags(mp, trace_flags, &enabled_trace_flags,
                                          &disabled_trace_flags);
 
@@ -1156,13 +1156,15 @@ COptTasks::Optimize(Query *query)
 //
 //---------------------------------------------------------------------------
 PlannedStmt *
-COptTasks::GPOPTOptimizedPlan(Query *query, SOptContext *gpopt_context)
+COptTasks::GPOPTOptimizedPlan(Query *query, SOptContext *gpopt_context, 
OptimizerOptions *opts)
 {
        Assert(query);
        Assert(gpopt_context);
 
        gpopt_context->m_query = query;
        gpopt_context->m_should_generate_plan_stmt = true;
+       // Copy options in `OptimizerOptions` to `SOptContext`
+       gpopt_context->m_create_vec_plan = opts->create_vectorization_plan;
        Execute(&OptimizeTask, gpopt_context);
        return gpopt_context->m_plan_stmt;
 }
diff --git a/src/backend/gporca/libgpdbcost/include/gpdbcost/CCostModelGPDB.h 
b/src/backend/gporca/libgpdbcost/include/gpdbcost/CCostModelGPDB.h
index 9103a08ed8e..2b44693fa4e 100644
--- a/src/backend/gporca/libgpdbcost/include/gpdbcost/CCostModelGPDB.h
+++ b/src/backend/gporca/libgpdbcost/include/gpdbcost/CCostModelGPDB.h
@@ -107,6 +107,12 @@ private:
                                                                         const 
CCostModelGPDB *pcmgpdb,
                                                                         const 
SCostingInfo *pci);
 
+       // cost of hash sequence project
+       static CCost CostHashSequenceProject(CMemoryPool *mp, 
+                                                                       
CExpressionHandle &exprhdl,
+                                                                       const 
CCostModelGPDB *pcmgpdb,
+                                                                       const 
SCostingInfo *pci);
+
        // cost of CTE producer
        static CCost CostCTEProducer(CMemoryPool *mp, CExpressionHandle 
&exprhdl,
                                                                 const 
CCostModelGPDB *pcmgpdb,
diff --git a/src/backend/gporca/libgpdbcost/src/CCostModelGPDB.cpp 
b/src/backend/gporca/libgpdbcost/src/CCostModelGPDB.cpp
index a91fc4283fd..73330059c72 100644
--- a/src/backend/gporca/libgpdbcost/src/CCostModelGPDB.cpp
+++ b/src/backend/gporca/libgpdbcost/src/CCostModelGPDB.cpp
@@ -31,6 +31,7 @@
 #include "gpopt/operators/CPhysicalMotionBroadcast.h"
 #include "gpopt/operators/CPhysicalPartitionSelector.h"
 #include "gpopt/operators/CPhysicalSequenceProject.h"
+#include "gpopt/operators/CPhysicalHashSequenceProject.h"
 #include "gpopt/operators/CPhysicalStreamAgg.h"
 #include "gpopt/operators/CPhysicalUnionAll.h"
 #include "gpopt/operators/CPredicateUtils.h"
@@ -1594,11 +1595,17 @@ CCostModelGPDB::CostSequenceProject(CMemoryPool *mp, 
CExpressionHandle &exprhdl,
        GPOS_ASSERT(COperator::EopPhysicalSequenceProject ==
                                exprhdl.Pop()->Eopid());
 
+       const CDouble dTupDefaultProcCostUnit =
+               pcmgpdb->GetCostModelParams()
+                       
->PcpLookup(CCostModelParamsGPDB::EcpTupDefaultProcCostUnit)
+                       ->Get();
+       GPOS_ASSERT(0 < dTupDefaultProcCostUnit);
+
        CPhysicalSequenceProject *psp = 
CPhysicalSequenceProject::PopConvert(exprhdl.Pop());
 
-       if (GPOS_FTRACE(EopttraceForceSplitWindowFunc) && 
+       if (GPOS_FTRACE(EopttraceForceSplitWindowFunc) &&
                psp->Pspt() == COperator::EsptypeGlobalTwoStep) {
-               return CCost(0);
+               return CCost(dTupDefaultProcCostUnit * 2);
        }
 
        const DOUBLE num_rows_outer = pci->PdRows()[0];
@@ -1614,12 +1621,59 @@ CCostModelGPDB::CostSequenceProject(CMemoryPool *mp, 
CExpressionHandle &exprhdl,
                ulSortCols += pos->UlSortColumns();
        }
 
+       // we process (sorted window of) input tuples to compute window 
function values
+       CCost costLocal =
+               CCost(pci->NumRebinds() * (ulSortCols * num_rows_outer * 
dWidthOuter *
+                                                                  
dTupDefaultProcCostUnit));
+       CCost costChild =
+               CostChildren(mp, exprhdl, pci, pcmgpdb->GetCostModelParams());
+       
+       return costLocal + costChild;
+}
+
+//---------------------------------------------------------------------------
+//     @function:
+//             CCostModelGPDB::CostSequenceProject
+//
+//     @doc:
+//             Cost of sequence project
+//
+//---------------------------------------------------------------------------
+CCost
+CCostModelGPDB::CostHashSequenceProject(CMemoryPool *mp, CExpressionHandle 
&exprhdl,
+                                                                       const 
CCostModelGPDB *pcmgpdb,
+                                                                       const 
SCostingInfo *pci)
+{
+       GPOS_ASSERT(nullptr != pcmgpdb);
+       GPOS_ASSERT(nullptr != pci);
+       GPOS_ASSERT(COperator::EopPhysicalHashSequenceProject ==
+                               exprhdl.Pop()->Eopid());
        const CDouble dTupDefaultProcCostUnit =
                pcmgpdb->GetCostModelParams()
                        
->PcpLookup(CCostModelParamsGPDB::EcpTupDefaultProcCostUnit)
                        ->Get();
        GPOS_ASSERT(0 < dTupDefaultProcCostUnit);
 
+       CPhysicalHashSequenceProject *psp = 
CPhysicalHashSequenceProject::PopConvert(exprhdl.Pop());
+
+       if (GPOS_FTRACE(EopttraceForceSplitWindowFunc) && 
+               psp->Pspt() == COperator::EsptypeGlobalTwoStep) {
+               return CCost(dTupDefaultProcCostUnit);
+       }
+
+       const DOUBLE num_rows_outer = pci->PdRows()[0];
+       const DOUBLE dWidthOuter = pci->GetWidth()[0];
+
+       ULONG ulSortCols = 0;
+       COrderSpecArray *pdrgpos =
+               CPhysicalHashSequenceProject::PopConvert(psp)->Pdrgpos();
+       const ULONG ulOrderSpecs = pdrgpos->Size();
+       for (ULONG ul = 0; ul < ulOrderSpecs; ul++)
+       {
+               COrderSpec *pos = (*pdrgpos)[ul];
+               ulSortCols += pos->UlSortColumns();
+       }
+
        // we process (sorted window of) input tuples to compute window 
function values
        CCost costLocal =
                CCost(pci->NumRebinds() * (ulSortCols * num_rows_outer * 
dWidthOuter *
@@ -2462,18 +2516,26 @@ CCostModelGPDB::Cost(
                        return CostSequenceProject(m_mp, exprhdl, this, pci);
                }
 
+               case COperator::EopPhysicalHashSequenceProject:
+               {
+                       return CostHashSequenceProject(m_mp, exprhdl, this, 
pci);
+               }
+
                case COperator::EopPhysicalCTEProducer:
                {
                        return CostCTEProducer(m_mp, exprhdl, this, pci);
                }
+
                case COperator::EopPhysicalCTEConsumer:
                {
                        return CostCTEConsumer(m_mp, exprhdl, this, pci);
                }
+
                case COperator::EopPhysicalConstTableGet:
                {
                        return CostConstTableGet(m_mp, exprhdl, this, pci);
                }
+
                case COperator::EopPhysicalDML:
                {
                        return CostDML(m_mp, exprhdl, this, pci);
diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpec.h 
b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpec.h
index d8b4919674b..d3b47b95f80 100644
--- a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpec.h
+++ b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpec.h
@@ -179,6 +179,9 @@ public:
        }
        // print
        IOstream &OsPrint(IOstream &os) const override = 0;
+       virtual IOstream &OsPrintNoWrap(IOstream &os) const{
+               return OsPrint(os);
+       }
 
        // return distribution partitioning type
        virtual EDistributionPartitioningType Edpt() const = 0;
diff --git 
a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h 
b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h
index 306b36f4ab9..5e4de6ac62a 100644
--- a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h
+++ b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h
@@ -178,7 +178,8 @@ public:
 
        // print
        IOstream &OsPrint(IOstream &os) const override;
-       IOstream &OsPrintWithPrefix(IOstream &os, const char *prefix) const;
+       IOstream &OsPrintNoWrap(IOstream &os) const override;
+       IOstream &OsPrintWithPrefix(IOstream &os, const char *prefix, BOOL wrap 
= true) const;
 
        // return a hashed distribution on the maximal hashable subset of given 
columns
        static CDistributionSpecHashed *PdshashedMaximal(CMemoryPool *mp,
diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/COperator.h 
b/src/backend/gporca/libgpopt/include/gpopt/operators/COperator.h
index 272bba90929..657e3082ef7 100644
--- a/src/backend/gporca/libgpopt/include/gpopt/operators/COperator.h
+++ b/src/backend/gporca/libgpopt/include/gpopt/operators/COperator.h
@@ -213,6 +213,7 @@ public:
                EopPhysicalCTEProducer,
                EopPhysicalCTEConsumer,
                EopPhysicalSequenceProject,
+               EopPhysicalHashSequenceProject,
                EopPhysicalDynamicIndexScan,
 
                EopPhysicalInnerHashJoin,
diff --git 
a/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashSequenceProject.h
 
b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashSequenceProject.h
new file mode 100644
index 00000000000..288c0873b47
--- /dev/null
+++ 
b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashSequenceProject.h
@@ -0,0 +1,123 @@
+/*-------------------------------------------------------------------------
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * CPhysicalHashSequenceProject.h
+ *
+ * IDENTIFICATION
+ *       
src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashSequenceProject.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef GPOPT_CPhysicalHashSequenceProject_H
+#define GPOPT_CPhysicalHashSequenceProject_H
+
+#include "gpos/base.h"
+
+#include "gpopt/base/CWindowFrame.h"
+#include "gpopt/operators/CPhysicalSequenceProject.h"
+
+namespace gpopt
+{
+// fwd declarations
+class CDistributionSpec;
+
+//---------------------------------------------------------------------------
+//     @class:
+//             CPhysicalHashSequenceProject
+//
+//     @doc:
+//             Physical Hash Sequence Project operator
+//
+//---------------------------------------------------------------------------
+class CPhysicalHashSequenceProject : public CPhysicalSequenceProject
+{
+public:
+       CPhysicalHashSequenceProject(const CPhysicalHashSequenceProject &) = 
delete;
+
+       // ctor
+       CPhysicalHashSequenceProject(CMemoryPool *mp, ESPType m_sptype,
+                                                                
CDistributionSpec *pds,
+                                                                
COrderSpecArray *pdrgpos,
+                                                                
CWindowFrameArray *pdrgpwf);
+
+       // dtor
+       ~CPhysicalHashSequenceProject() override;
+
+       // ident accessors
+       EOperatorId
+       Eopid() const override
+       {
+               return EopPhysicalHashSequenceProject;
+       }
+
+       // operator name
+       const CHAR *
+       SzId() const override
+       {
+               return "CPhysicalHashSequenceProject";
+       }
+
+       // match function
+       BOOL Matches(COperator *pop) const override;
+
+       // hashing function
+       ULONG HashValue() const override;
+
+       // sensitivity to order of inputs
+       BOOL FInputOrderSensitive() const override;
+
+       // compute required sort order of the n-th child
+       COrderSpec *PosRequired(CMemoryPool *mp, CExpressionHandle &exprhdl,
+                                                       COrderSpec 
*posRequired, ULONG child_index,
+                                                       CDrvdPropArray 
*pdrgpdpCtxt,
+                                                       ULONG ulOptReq) const 
override;
+
+       // return order property enforcing type for this operator
+       CEnfdProp::EPropEnforcingType EpetOrder(
+               CExpressionHandle &exprhdl, const CEnfdOrder *peo) const 
override;
+
+       // return true if operator passes through stats obtained from children,
+       // this is used when computing stats during costing
+       BOOL
+       FPassThruStats() const override
+       {
+               return true;
+       }
+
+       // print
+       IOstream &OsPrint(IOstream &os) const override;
+
+       // conversion function
+       static CPhysicalHashSequenceProject *
+       PopConvert(COperator *pop)
+       {
+               GPOS_ASSERT(nullptr != pop);
+               GPOS_ASSERT(EopPhysicalHashSequenceProject == pop->Eopid());
+
+               return dynamic_cast<CPhysicalHashSequenceProject *>(pop);
+       }
+
+};     // class CPhysicalHashSequenceProject
+
+}  // namespace gpopt
+
+#endif // !GPOPT_CPhysicalHashSequenceProject_H
+
+// EOF
diff --git 
a/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalSequenceProject.h
 
b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalSequenceProject.h
index dc6afd1ecfa..e9d84d6edad 100644
--- 
a/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalSequenceProject.h
+++ 
b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalSequenceProject.h
@@ -31,7 +31,7 @@ class CDistributionSpec;
 //---------------------------------------------------------------------------
 class CPhysicalSequenceProject : public CPhysical
 {
-private:
+protected:
        // window type
        ESPType m_sptype;
 
diff --git 
a/src/backend/gporca/libgpopt/include/gpopt/translate/CTranslatorExprToDXL.h 
b/src/backend/gporca/libgpopt/include/gpopt/translate/CTranslatorExprToDXL.h
index a3acaebb254..2c50e9e812f 100644
--- a/src/backend/gporca/libgpopt/include/gpopt/translate/CTranslatorExprToDXL.h
+++ b/src/backend/gporca/libgpopt/include/gpopt/translate/CTranslatorExprToDXL.h
@@ -244,7 +244,7 @@ private:
 
        CDXLNode *PdxlnWindow(CExpression *pexprSeqPrj, CColRefArray 
*colref_array,
                                                  CDistributionSpecArray 
*pdrgpdsBaseTables,
-                                                 ULONG *pulNonGatherMotions, 
BOOL *pfDML);
+                                                 ULONG *pulNonGatherMotions, 
BOOL *pfDML, BOOL fWindowHashAgg);
 
        CDXLNode *PdxlnNLJoin(CExpression *pexprNLJ, CColRefArray *colref_array,
                                                  CDistributionSpecArray 
*pdrgpdsBaseTables,
diff --git a/src/backend/gporca/libgpopt/include/gpopt/xforms/CXform.h 
b/src/backend/gporca/libgpopt/include/gpopt/xforms/CXform.h
index fc7a1c8c7e6..904e6b4b1c8 100644
--- a/src/backend/gporca/libgpopt/include/gpopt/xforms/CXform.h
+++ b/src/backend/gporca/libgpopt/include/gpopt/xforms/CXform.h
@@ -170,6 +170,7 @@ public:
                ExfSplitDQA,
                ExfSequenceProject2Apply,
                ExfImplementSequenceProject,
+               ExfImplementHashSequenceProject,
                ExfImplementAssert,
                ExfCTEAnchor2Sequence,
                ExfCTEAnchor2TrivialSelect,
diff --git 
a/src/backend/gporca/libgpopt/include/gpopt/xforms/CXformImplementHashSequenceProject.h
 
b/src/backend/gporca/libgpopt/include/gpopt/xforms/CXformImplementHashSequenceProject.h
new file mode 100644
index 00000000000..1422a1bbaec
--- /dev/null
+++ 
b/src/backend/gporca/libgpopt/include/gpopt/xforms/CXformImplementHashSequenceProject.h
@@ -0,0 +1,95 @@
+/*-------------------------------------------------------------------------
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * CXformImplementHashSequenceProject.h
+ *
+ * IDENTIFICATION
+ *       
src/backend/gporca/libgpopt/include/gpopt/xforms/CXformImplementHashSequenceProject.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef GPOPT_CXformImplementHashSequenceProject_H
+#define GPOPT_CXformImplementHashSequenceProject_H
+
+#include "gpos/base.h"
+
+#include "gpopt/operators/CExpressionHandle.h"
+#include "gpopt/xforms/CXformImplementation.h"
+
+namespace gpopt
+{
+using namespace gpos;
+
+//---------------------------------------------------------------------------
+//     @class:
+//             CXformImplementHashSequenceProject
+//
+//     @doc:
+//             Transform Project to ComputeScalar
+//
+//---------------------------------------------------------------------------
+class CXformImplementHashSequenceProject : public CXformImplementation
+{
+private:
+public:
+       CXformImplementHashSequenceProject(
+               const CXformImplementHashSequenceProject &) = delete;
+
+       // ctor
+       explicit CXformImplementHashSequenceProject(CMemoryPool *mp);
+
+       // dtor
+       ~CXformImplementHashSequenceProject() override = default;
+
+       // ident accessors
+       EXformId
+       Exfid() const override
+       {
+               return ExfImplementHashSequenceProject;
+       }
+
+       const CHAR *
+       SzId() const override
+       {
+               return "CXformImplementHashSequenceProject";
+       }
+
+       // compute xform promise for a given expression handle
+       EXformPromise
+       Exfp(CExpressionHandle &exprhdl) const override
+       {
+               if (exprhdl.DeriveHasSubquery(1))
+               {
+                       return CXform::ExfpNone;
+               }
+
+               return CXform::ExfpHigh;
+       }
+
+       // actual transform
+       void Transform(CXformContext *, CXformResult *,
+                                  CExpression *) const override;
+
+};     // class CXformImplementHashSequenceProject
+
+}  // namespace gpopt
+
+#endif // !GPOPT_CXformImplementHashSequenceProject_H
+
+// EOF
diff --git a/src/backend/gporca/libgpopt/include/gpopt/xforms/xforms.h 
b/src/backend/gporca/libgpopt/include/gpopt/xforms/xforms.h
index 25e724c2128..d0972b2be2c 100644
--- a/src/backend/gporca/libgpopt/include/gpopt/xforms/xforms.h
+++ b/src/backend/gporca/libgpopt/include/gpopt/xforms/xforms.h
@@ -71,6 +71,7 @@
 #include "gpopt/xforms/CXformImplementLimit.h"
 #include "gpopt/xforms/CXformImplementSequence.h"
 #include "gpopt/xforms/CXformImplementSequenceProject.h"
+#include "gpopt/xforms/CXformImplementHashSequenceProject.h"
 #include "gpopt/xforms/CXformImplementSplit.h"
 #include "gpopt/xforms/CXformImplementTVF.h"
 #include "gpopt/xforms/CXformImplementTVFNoArgs.h"
diff --git a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp 
b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp
index 7636b0d74d4..0adf07022d8 100644
--- a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp
+++ b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp
@@ -858,9 +858,16 @@ CDistributionSpecHashed::OsPrint(IOstream &os) const
        return OsPrintWithPrefix(os, "");
 }
 
+IOstream &
+CDistributionSpecHashed::OsPrintNoWrap(IOstream &os) const
+{
+       return OsPrintWithPrefix(os, "", false);
+}
+
 IOstream &
 CDistributionSpecHashed::OsPrintWithPrefix(IOstream &os,
-                                                                               
   const char *prefix) const
+                                                                               
   const char *prefix,
+                                                                               
   BOOL wrap) const
 {
        os << this->SzId() << ": [ ";
        const ULONG length = m_pdrgpexpr->Size();
@@ -876,7 +883,7 @@ CDistributionSpecHashed::OsPrintWithPrefix(IOstream &os,
                }
                else
                {
-                       os << *(hash_expr);
+                       hash_expr->OsPrint(os);
                        // the expression added a newline, indent the following 
with the prefix
                        os << prefix;
                }
@@ -912,7 +919,8 @@ CDistributionSpecHashed::OsPrintWithPrefix(IOstream &os,
        if (nullptr != m_equiv_hash_exprs && m_equiv_hash_exprs->Size() > 0 &&
                GPOS_FTRACE(EopttracePrintEquivDistrSpecs))
        {
-               os << "," << std::endl;
+               os << ",";
+               if (wrap) os << std::endl;
                for (ULONG ul = 0; ul < m_equiv_hash_exprs->Size(); ul++)
                {
                        CExpressionArray *equiv_distribution_exprs =
diff --git 
a/src/backend/gporca/libgpopt/src/operators/CLogicalSequenceProject.cpp 
b/src/backend/gporca/libgpopt/src/operators/CLogicalSequenceProject.cpp
index f0018ea02a8..4d20c7c5e26 100644
--- a/src/backend/gporca/libgpopt/src/operators/CLogicalSequenceProject.cpp
+++ b/src/backend/gporca/libgpopt/src/operators/CLogicalSequenceProject.cpp
@@ -369,6 +369,8 @@ CLogicalSequenceProject::PxfsCandidates(CMemoryPool *mp) 
const
        CXformSet *xform_set = GPOS_NEW(mp) CXformSet(mp);
        (void) xform_set->ExchangeSet(CXform::ExfSequenceProject2Apply);
        (void) xform_set->ExchangeSet(CXform::ExfImplementSequenceProject);
+       if (GPOS_FTRACE(EopttraceEnableWindowHashAgg))
+               (void) 
xform_set->ExchangeSet(CXform::ExfImplementHashSequenceProject);
 
        return xform_set;
 }
diff --git 
a/src/backend/gporca/libgpopt/src/operators/CPhysicalHashSequenceProject.cpp 
b/src/backend/gporca/libgpopt/src/operators/CPhysicalHashSequenceProject.cpp
new file mode 100644
index 00000000000..4a6fee19a81
--- /dev/null
+++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalHashSequenceProject.cpp
@@ -0,0 +1,151 @@
+/*-------------------------------------------------------------------------
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * CPhysicalHashSequenceProject.cpp
+ *
+ * IDENTIFICATION
+ *       
src/backend/gporca/libgpopt/src/operators/CPhysicalHashSequenceProject.cpp
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "gpopt/operators/CPhysicalHashSequenceProject.h"
+
+#include "gpos/base.h"
+
+#include "gpopt/base/CDistributionSpecAny.h"
+#include "gpopt/base/CDistributionSpecHashed.h"
+#include "gpopt/base/CDistributionSpecReplicated.h"
+#include "gpopt/base/CDistributionSpecSingleton.h"
+#include "gpopt/base/COptCtxt.h"
+#include "gpopt/base/CUtils.h"
+#include "gpopt/base/CWindowFrame.h"
+#include "gpopt/cost/ICostModel.h"
+#include "gpopt/operators/CExpressionHandle.h"
+#include "gpopt/operators/CLogicalSequenceProject.h"
+#include "gpopt/operators/CScalarIdent.h"
+
+using namespace gpopt;
+
+//---------------------------------------------------------------------------
+//     @function:
+//             CPhysicalHashSequenceProject::CPhysicalHashSequenceProject
+//
+//     @doc:
+//             Ctor
+//
+//---------------------------------------------------------------------------
+CPhysicalHashSequenceProject::CPhysicalHashSequenceProject(CMemoryPool *mp,
+                                                                               
                   ESPType sptype,
+                                                                               
                   CDistributionSpec *pds,
+                                                                               
                   COrderSpecArray *pdrgpos,
+                                                                               
                   CWindowFrameArray *pdrgpwf)
+       : CPhysicalSequenceProject(mp, sptype, pds, pdrgpos, pdrgpwf) 
+{}
+
+CPhysicalHashSequenceProject::~CPhysicalHashSequenceProject() {}
+
+COrderSpec *
+CPhysicalHashSequenceProject::PosRequired(CMemoryPool *mp,
+                                                                         
CExpressionHandle &,  // exprhdl
+                                                                         
COrderSpec *,                 // posRequired
+                                                                         ULONG
+#ifdef GPOS_DEBUG
+                                                                               
  child_index
+#endif // GPOS_DEBUG
+                                                                         ,
+                                                                         
CDrvdPropArray *,      // pdrgpdpCtxt
+                                                                         ULONG 
                         // ulOptReq
+) const
+{
+       GPOS_ASSERT(0 == child_index);
+       return GPOS_NEW(mp) COrderSpec(mp);
+}
+
+// match function
+BOOL 
+CPhysicalHashSequenceProject::Matches(COperator *pop) const
+{
+       GPOS_ASSERT(nullptr != pop);
+       if (Eopid() == pop->Eopid())
+       {
+               CPhysicalHashSequenceProject *popHashSequenceProject =
+                       CPhysicalHashSequenceProject::PopConvert(pop);
+               return m_sptype == popHashSequenceProject->Pspt() &&
+                          m_pds->Matches(popHashSequenceProject->Pds()) &&
+                          CWindowFrame::Equals(m_pdrgpwf,
+                                                                       
popHashSequenceProject->Pdrgpwf()) &&
+                          COrderSpec::Equals(m_pdrgpos,
+                                                                 
popHashSequenceProject->Pdrgpos());
+       }
+
+       return false;
+}
+
+// hashing function
+
+ULONG
+CPhysicalHashSequenceProject::HashValue() const
+{
+       BOOL ltrue = true;
+       ULONG ulHash = CPhysicalSequenceProject::HashValue();
+       
+       ulHash = gpos::CombineHashes(ulHash, m_pds->HashValue());
+       // combine a true hash value
+       ulHash = gpos::CombineHashes(ulHash, gpos::HashValue<BOOL>(&ltrue));
+
+       return ulHash;
+}
+
+BOOL
+CPhysicalHashSequenceProject::FInputOrderSensitive() const {
+       return false;
+}
+
+CEnfdProp::EPropEnforcingType
+CPhysicalHashSequenceProject::EpetOrder(CExpressionHandle &/*exprhdl*/,
+                                                                       const 
CEnfdOrder *
+#ifdef GPOS_DEBUG
+                                                                       peo
+#endif // GPOS_DEBUG
+) const
+{
+       GPOS_ASSERT(nullptr != peo);
+       GPOS_ASSERT(!peo->PosRequired()->IsEmpty());
+
+       return CEnfdProp::EpetRequired;
+}
+
+
+IOstream &CPhysicalHashSequenceProject::OsPrint(IOstream &os) const
+{
+       os << SzId() << " (";
+       CLogicalSequenceProject::OsPrintWindowType(os, m_sptype);
+       os << ", hashed) (";
+       (void) m_pds->OsPrint(os);
+       os << ", ";
+       (void) COrderSpec::OsPrint(os, m_pdrgpos);
+       os << ", ";
+       (void) CWindowFrame::OsPrint(os, m_pdrgpwf);
+
+       return os << ")";
+}
+
+
+// EOF
diff --git 
a/src/backend/gporca/libgpopt/src/operators/CPhysicalSequenceProject.cpp 
b/src/backend/gporca/libgpopt/src/operators/CPhysicalSequenceProject.cpp
index 4b663137cf4..3c5aade6eb9 100644
--- a/src/backend/gporca/libgpopt/src/operators/CPhysicalSequenceProject.cpp
+++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalSequenceProject.cpp
@@ -563,13 +563,12 @@ CPhysicalSequenceProject::OsPrint(IOstream &os) const
 {
        os << SzId() << " (";
        CLogicalSequenceProject::OsPrintWindowType(os, m_sptype);
-       os << ") (";
-       (void) m_pds->OsPrint(os);
+       os << ", non-hashed) (";
+       (void) m_pds->OsPrintNoWrap(os);
        os << ", ";
        (void) COrderSpec::OsPrint(os, m_pdrgpos);
        os << ", ";
        (void) CWindowFrame::OsPrint(os, m_pdrgpwf);
-
        return os << ")";
 }
 
diff --git a/src/backend/gporca/libgpopt/src/operators/Makefile 
b/src/backend/gporca/libgpopt/src/operators/Makefile
index 5030aec79a0..faa0f9a70d6 100644
--- a/src/backend/gporca/libgpopt/src/operators/Makefile
+++ b/src/backend/gporca/libgpopt/src/operators/Makefile
@@ -130,6 +130,7 @@ OBJS        = CExpression.o \
               CPhysicalScan.o \
               CPhysicalSequence.o \
               CPhysicalSequenceProject.o \
+              CPhysicalHashSequenceProject.o \
               CPhysicalSerialUnionAll.o \
               CPhysicalSort.o \
               CPhysicalSplit.o \
diff --git a/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp 
b/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp
index fa1e7a1d17c..6119e2ba71f 100644
--- a/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp
+++ b/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp
@@ -58,6 +58,7 @@
 #include "gpopt/operators/CPhysicalPartitionSelector.h"
 #include "gpopt/operators/CPhysicalScalarAgg.h"
 #include "gpopt/operators/CPhysicalSequenceProject.h"
+#include "gpopt/operators/CPhysicalHashSequenceProject.h"
 #include "gpopt/operators/CPhysicalSort.h"
 #include "gpopt/operators/CPhysicalSplit.h"
 #include "gpopt/operators/CPhysicalSpool.h"
@@ -313,8 +314,6 @@ CTranslatorExprToDXL::PdxlnTranslate(CExpression *pexpr,
                pdxlnPrL->ReplaceChild(ul, pdxlnPrElNew);
        }
 
-
-
        if (0 == ulNonGatherMotions)
        {
                CTranslatorExprToDXLUtils::SetDirectDispatchInfo(
@@ -409,10 +408,11 @@ CTranslatorExprToDXL::CreateDXLNode(CExpression *pexpr,
                                pexpr, colref_array, pdrgpdsBaseTables, 
pulNonGatherMotions,
                                pfDML);
                        break;
+               case COperator::EopPhysicalHashSequenceProject:
                case COperator::EopPhysicalSequenceProject:
                        dxlnode = CTranslatorExprToDXL::PdxlnWindow(
                                pexpr, colref_array, pdrgpdsBaseTables, 
pulNonGatherMotions,
-                               pfDML);
+                               pfDML, pexpr->Pop()->Eopid() == 
COperator::EopPhysicalHashSequenceProject);
                        break;
                case COperator::EopPhysicalInnerNLJoin:
                case COperator::EopPhysicalInnerIndexNLJoin:
@@ -6854,11 +6854,13 @@ CDXLNode *
 CTranslatorExprToDXL::PdxlnWindow(CExpression *pexprSeqPrj,
                                                                  CColRefArray 
*colref_array,
                                                                  
CDistributionSpecArray *pdrgpdsBaseTables,
-                                                                 ULONG 
*pulNonGatherMotions, BOOL *pfDML)
+                                                                 ULONG 
*pulNonGatherMotions, BOOL *pfDML,
+                                                                 BOOL 
fWindowHashAgg)
 {
        GPOS_ASSERT(nullptr != pexprSeqPrj);
 
-       CPhysicalSequenceProject *popSeqPrj =
+       CPhysicalSequenceProject *popSeqPrj = fWindowHashAgg ? 
+               CPhysicalHashSequenceProject::PopConvert(pexprSeqPrj->Pop()) :
                CPhysicalSequenceProject::PopConvert(pexprSeqPrj->Pop());
        CDistributionSpec *pds = popSeqPrj->Pds();
        ULongPtrArray *colids = GPOS_NEW(m_mp) ULongPtrArray(m_mp);
@@ -6940,7 +6942,8 @@ CTranslatorExprToDXL::PdxlnWindow(CExpression 
*pexprSeqPrj,
 
        // construct a Window node
        CDXLPhysicalWindow *pdxlopWindow =
-               GPOS_NEW(m_mp) CDXLPhysicalWindow(m_mp, colids, pdrgpdxlwk);
+               GPOS_NEW(m_mp) CDXLPhysicalWindow(m_mp, colids, pdrgpdxlwk,
+                       fWindowHashAgg);
        CDXLNode *pdxlnWindow = GPOS_NEW(m_mp) CDXLNode(m_mp, pdxlopWindow);
        pdxlnWindow->SetProperties(dxl_properties);
 
@@ -6958,7 +6961,6 @@ CTranslatorExprToDXL::PdxlnWindow(CExpression 
*pexprSeqPrj,
        return pdxlnWindow;
 }
 
-
 //---------------------------------------------------------------------------
 //     @function:
 //             CTranslatorExprToDXL::PdxlnArray
diff --git a/src/backend/gporca/libgpopt/src/xforms/CXformFactory.cpp 
b/src/backend/gporca/libgpopt/src/xforms/CXformFactory.cpp
index 27267cc39c8..f54aa957ebc 100644
--- a/src/backend/gporca/libgpopt/src/xforms/CXformFactory.cpp
+++ b/src/backend/gporca/libgpopt/src/xforms/CXformFactory.cpp
@@ -248,6 +248,7 @@ CXformFactory::Instantiate()
        Add(GPOS_NEW(m_mp) CXformSplitDQA(m_mp));
        Add(GPOS_NEW(m_mp) CXformSequenceProject2Apply(m_mp));
        Add(GPOS_NEW(m_mp) CXformImplementSequenceProject(m_mp));
+       Add(GPOS_NEW(m_mp) CXformImplementHashSequenceProject(m_mp));
        Add(GPOS_NEW(m_mp) CXformImplementAssert(m_mp));
        Add(GPOS_NEW(m_mp) CXformCTEAnchor2Sequence(m_mp));
        Add(GPOS_NEW(m_mp) CXformCTEAnchor2TrivialSelect(m_mp));
diff --git 
a/src/backend/gporca/libgpopt/src/xforms/CXformImplementHashSequenceProject.cpp 
b/src/backend/gporca/libgpopt/src/xforms/CXformImplementHashSequenceProject.cpp
new file mode 100644
index 00000000000..e01f95377fb
--- /dev/null
+++ 
b/src/backend/gporca/libgpopt/src/xforms/CXformImplementHashSequenceProject.cpp
@@ -0,0 +1,112 @@
+/*-------------------------------------------------------------------------
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * CXformImplementHashSequenceProject.cpp
+ *
+ * IDENTIFICATION
+ *       
src/backend/gporca/libgpopt/src/xforms/CXformImplementHashSequenceProject.cpp
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "gpopt/xforms/CXformImplementHashSequenceProject.h"
+
+#include "gpos/base.h"
+
+#include "gpopt/operators/CLogicalSequenceProject.h"
+#include "gpopt/operators/CPatternLeaf.h"
+#include "gpopt/operators/CPhysicalHashSequenceProject.h"
+
+
+using namespace gpopt;
+
+
+//---------------------------------------------------------------------------
+//     @function:
+//             
CXformImplementHashSequenceProject::CXformImplementHashSequenceProject
+//
+//     @doc:
+//             Ctor
+//
+//---------------------------------------------------------------------------
+CXformImplementHashSequenceProject::CXformImplementHashSequenceProject(
+       CMemoryPool *mp)
+       :  // pattern
+         CXformImplementation(GPOS_NEW(mp) CExpression(
+                 mp, GPOS_NEW(mp) CLogicalSequenceProject(mp),
+                 GPOS_NEW(mp) CExpression(
+                         mp, GPOS_NEW(mp) CPatternLeaf(mp)),  // relational 
child
+                 GPOS_NEW(mp)
+                         CExpression(mp, GPOS_NEW(mp) CPatternLeaf(mp))  // 
scalar child
+                 ))
+{
+}
+
+
+//---------------------------------------------------------------------------
+//     @function:
+//             CXformImplementHashSequenceProject::Transform
+//
+//     @doc:
+//             Actual transformation
+//
+//---------------------------------------------------------------------------
+void
+CXformImplementHashSequenceProject::Transform(CXformContext *pxfctxt,
+                                                                               
          CXformResult *pxfres,
+                                                                               
          CExpression *pexpr) const
+{
+       GPOS_ASSERT(nullptr != pxfctxt);
+       GPOS_ASSERT(FPromising(pxfctxt->Pmp(), this, pexpr));
+       GPOS_ASSERT(FCheckPattern(pexpr));
+
+       CMemoryPool *mp = pxfctxt->Pmp();
+
+       // extract components
+       CExpression *pexprRelational = (*pexpr)[0];
+       CExpression *pexprScalar = (*pexpr)[1];
+
+       // addref all children
+       pexprRelational->AddRef();
+       pexprScalar->AddRef();
+
+       // extract members of logical sequence project operator
+       CLogicalSequenceProject *popLogicalSequenceProject =
+               CLogicalSequenceProject::PopConvert(pexpr->Pop());
+       COperator::ESPType sptype = popLogicalSequenceProject->Pspt();
+       CDistributionSpec *pds = popLogicalSequenceProject->Pds();
+       COrderSpecArray *pdrgpos = popLogicalSequenceProject->Pdrgpos();
+       CWindowFrameArray *pdrgpwf = popLogicalSequenceProject->Pdrgpwf();
+       pds->AddRef();
+       pdrgpos->AddRef();
+       pdrgpwf->AddRef();
+
+       // assemble physical operator
+       CExpression *pexprSequenceProject = GPOS_NEW(mp) CExpression(
+               mp,
+               GPOS_NEW(mp)
+                       CPhysicalHashSequenceProject(mp, sptype, pds, pdrgpos, 
pdrgpwf),
+               pexprRelational, pexprScalar);
+
+       // add alternative to results
+       pxfres->Add(pexprSequenceProject);
+}
+
+
+// EOF
diff --git a/src/backend/gporca/libgpopt/src/xforms/Makefile 
b/src/backend/gporca/libgpopt/src/xforms/Makefile
index 4283daedb4c..a51ddd6f564 100644
--- a/src/backend/gporca/libgpopt/src/xforms/Makefile
+++ b/src/backend/gporca/libgpopt/src/xforms/Makefile
@@ -58,6 +58,7 @@ OBJS        = CDecorrelator.o \
               CXformImplementLimit.o \
               CXformImplementSequence.o \
               CXformImplementSequenceProject.o \
+              CXformImplementHashSequenceProject.o \
               CXformImplementSplit.o \
               CXformImplementTVF.o \
               CXformImplementTVFNoArgs.o \
diff --git 
a/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLPhysicalWindow.h
 
b/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLPhysicalWindow.h
index 4df4c3bc27a..a1d304af502 100644
--- 
a/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLPhysicalWindow.h
+++ 
b/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLPhysicalWindow.h
@@ -46,12 +46,14 @@ private:
        // window keys
        CDXLWindowKeyArray *m_dxl_window_key_array;
 
+       BOOL m_window_hash_agg;
+
 public:
        CDXLPhysicalWindow(CDXLPhysicalWindow &) = delete;
 
        //ctor
        CDXLPhysicalWindow(CMemoryPool *mp, ULongPtrArray *part_by_colid_array,
-                                          CDXLWindowKeyArray 
*window_key_array);
+                                          CDXLWindowKeyArray 
*window_key_array, BOOL fWindowHashAgg);
 
        //dtor
        ~CDXLPhysicalWindow() override;
@@ -76,6 +78,9 @@ public:
        // return the window key at a given position
        CDXLWindowKey *GetDXLWindowKeyAt(ULONG ulPos) const;
 
+       // is windowagg implements by window hash agg
+       BOOL IsWindowHashAgg() const;
+
        // serialize operator in DXL format
        void SerializeToDXL(CXMLSerializer *xml_serializer,
                                                const CDXLNode *dxlnode) const 
override;
diff --git 
a/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h 
b/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h
index f4daec1305f..a03f4bb92dd 100644
--- a/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h
+++ b/src/backend/gporca/libnaucrates/include/naucrates/dxl/xml/dxltokens.h
@@ -237,6 +237,7 @@ enum Edxltoken
        EdxltokenScalarWindowFrameTrailingEdge,
        EdxltokenWindowKeyList,
        EdxltokenWindowKey,
+       EdxltokenWindowHashAgg,
 
        EdxltokenWindowSpecList,
        EdxltokenWindowSpec,
diff --git 
a/src/backend/gporca/libnaucrates/include/naucrates/traceflags/traceflags.h 
b/src/backend/gporca/libnaucrates/include/naucrates/traceflags/traceflags.h
index 2e0995fcd66..f433b28d062 100644
--- a/src/backend/gporca/libnaucrates/include/naucrates/traceflags/traceflags.h
+++ b/src/backend/gporca/libnaucrates/include/naucrates/traceflags/traceflags.h
@@ -238,6 +238,9 @@ enum EOptTraceFlag
        // Disable dynamic seq/bitmap/index scan
        EopttraceDisableDynamicTableScan = 103049,
 
+       // Enable window hash agg
+       EopttraceEnableWindowHashAgg = 103050,
+
        ///////////////////////////////////////////////////////
        ///////////////////// statistics flags ////////////////
        //////////////////////////////////////////////////////
diff --git 
a/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalWindow.cpp 
b/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalWindow.cpp
index 9c5e7ca4e9b..19112cf23ca 100644
--- a/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalWindow.cpp
+++ b/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalWindow.cpp
@@ -28,10 +28,12 @@ using namespace gpdxl;
 //---------------------------------------------------------------------------
 CDXLPhysicalWindow::CDXLPhysicalWindow(CMemoryPool *mp,
                                                                           
ULongPtrArray *part_by_colid_array,
-                                                                          
CDXLWindowKeyArray *window_key_array)
+                                                                          
CDXLWindowKeyArray *window_key_array,
+                                                                          BOOL 
fWindowHashAgg)
        : CDXLPhysical(mp),
          m_part_by_colid_array(part_by_colid_array),
-         m_dxl_window_key_array(window_key_array)
+         m_dxl_window_key_array(window_key_array),
+         m_window_hash_agg(fWindowHashAgg)
 {
        GPOS_ASSERT(nullptr != m_part_by_colid_array);
        GPOS_ASSERT(nullptr != m_dxl_window_key_array);
@@ -122,6 +124,18 @@ CDXLPhysicalWindow::GetDXLWindowKeyAt(ULONG position) const
        return (*m_dxl_window_key_array)[position];
 }
 
+//---------------------------------------------------------------------------
+//     @function:
+//             CDXLPhysicalWindow::IsWindowHashAgg
+//
+//     @doc:
+//             Is windowagg implements by window hash agg
+//
+//---------------------------------------------------------------------------
+BOOL CDXLPhysicalWindow::IsWindowHashAgg() const {
+       return m_window_hash_agg;
+}
+
 //---------------------------------------------------------------------------
 //     @function:
 //             CDXLPhysicalWindow::SerializeToDXL
@@ -168,6 +182,8 @@ CDXLPhysicalWindow::SerializeToDXL(CXMLSerializer 
*xml_serializer,
                CDXLTokens::GetDXLTokenStr(EdxltokenNamespacePrefix),
                window_keys_list_str);
 
+       xml_serializer->AddAttribute(
+               CDXLTokens::GetDXLTokenStr(EdxltokenWindowHashAgg), 
m_window_hash_agg);
        xml_serializer->CloseElement(
                CDXLTokens::GetDXLTokenStr(EdxltokenNamespacePrefix), 
element_name);
 }
diff --git 
a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalWindow.cpp 
b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalWindow.cpp
index 50704f05d39..28801cbf271 100644
--- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalWindow.cpp
+++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalWindow.cpp
@@ -155,7 +155,7 @@ CParseHandlerPhysicalWindow::EndElement(const XMLCh *const, 
 // element_uri,
        CDXLWindowKeyArray *window_key_array_dxlnode =
                window_key_list_parse_handler->GetDxlWindowKeyArray();
        CDXLPhysicalWindow *window_op_dxlnode = GPOS_NEW(m_mp) 
CDXLPhysicalWindow(
-               m_mp, m_part_by_colid_array, window_key_array_dxlnode);
+               m_mp, m_part_by_colid_array, window_key_array_dxlnode, false);
        m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, window_op_dxlnode);
 
        // set statistics and physical properties
diff --git a/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp 
b/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp
index 4ddfaf6fbed..2d56b2fce23 100644
--- a/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp
+++ b/src/backend/gporca/libnaucrates/src/xml/dxltokens.cpp
@@ -343,6 +343,7 @@ CDXLTokens::Init(CMemoryPool *mp)
                {EdxltokenWindowFrame, GPOS_WSZ_LIT("WindowFrame")},
                {EdxltokenWindowKeyList, GPOS_WSZ_LIT("WindowKeyList")},
                {EdxltokenWindowKey, GPOS_WSZ_LIT("WindowKey")},
+               {EdxltokenWindowHashAgg, GPOS_WSZ_LIT("IsWindowHashAgg")},
 
                {EdxltokenWindowSpecList, GPOS_WSZ_LIT("WindowSpecList")},
                {EdxltokenWindowSpec, GPOS_WSZ_LIT("WindowSpec")},
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 18ffeb3f784..e9c44880cb4 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1473,6 +1473,43 @@ _copyWindowAgg(const WindowAgg *from)
        return newnode;
 }
 
+/*
+ * _copyWindowHashAgg
+ */
+static WindowHashAgg *
+_copyWindowHashAgg(const WindowHashAgg *from)
+{
+       WindowHashAgg  *newnode = makeNode(WindowHashAgg);
+
+       CopyPlanFields((const Plan *) from, (Plan *) newnode);
+
+       COPY_SCALAR_FIELD(winref);
+       COPY_SCALAR_FIELD(partNumCols);
+       COPY_POINTER_FIELD(partColIdx, from->partNumCols * sizeof(AttrNumber));
+       COPY_POINTER_FIELD(partOperators, from->partNumCols * sizeof(Oid));
+       COPY_POINTER_FIELD(partCollations, from->partNumCols * sizeof(Oid));
+       COPY_SCALAR_FIELD(ordNumCols);
+
+       if (from->ordNumCols > 0)
+       {
+               COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * 
sizeof(AttrNumber));
+               COPY_POINTER_FIELD(ordOperators, from->ordNumCols * 
sizeof(Oid));
+               COPY_POINTER_FIELD(ordCollations, from->ordNumCols * 
sizeof(Oid));
+               COPY_POINTER_FIELD(ordNullsFirst, from->ordNumCols * 
sizeof(bool));
+       }
+
+       COPY_SCALAR_FIELD(frameOptions);
+       COPY_NODE_FIELD(startOffset);
+       COPY_NODE_FIELD(endOffset);
+       COPY_SCALAR_FIELD(startInRangeFunc);
+       COPY_SCALAR_FIELD(endInRangeFunc);
+       COPY_SCALAR_FIELD(inRangeColl);
+       COPY_SCALAR_FIELD(inRangeAsc);
+       COPY_SCALAR_FIELD(inRangeNullsFirst);
+
+       return newnode;
+}
+
 /*
  * _copyUnique
  */
@@ -6562,6 +6599,9 @@ copyObjectImpl(const void *from)
                case T_WindowAgg:
                        retval = _copyWindowAgg(from);
                        break;
+               case T_WindowHashAgg:
+                       retval = _copyWindowHashAgg(from);
+                       break;
                case T_TableFunctionScan:
                        retval = _copyTableFunctionScan(from);
                        break;
diff --git a/src/backend/nodes/outfast.c b/src/backend/nodes/outfast.c
index 054fc06238a..cfc6a322c0d 100644
--- a/src/backend/nodes/outfast.c
+++ b/src/backend/nodes/outfast.c
@@ -1030,6 +1030,9 @@ _outNode(StringInfo str, void *obj)
                        case T_WindowAgg:
                                _outWindowAgg(str, obj);
                                break;
+                       case T_WindowHashAgg:
+                               _outWindowHashAgg(str, obj);
+                               break;
                        case T_Group:
                                _outGroup(str, obj);
                                break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 38f34f40b62..6b21ff93654 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1013,6 +1013,33 @@ _outWindowAgg(StringInfo str, const WindowAgg *node)
        WRITE_BOOL_FIELD(inRangeNullsFirst);
 }
 
+static void
+_outWindowHashAgg(StringInfo str, const WindowHashAgg *node)
+{
+       WRITE_NODE_TYPE("WINDOWHASHAGG");
+
+       _outPlanInfo(str, (const Plan *) node);
+
+       WRITE_UINT_FIELD(winref);
+       WRITE_INT_FIELD(partNumCols);
+       WRITE_ATTRNUMBER_ARRAY(partColIdx, node->partNumCols);
+       WRITE_OID_ARRAY(partOperators, node->partNumCols);
+       WRITE_OID_ARRAY(partCollations, node->partNumCols);
+       WRITE_INT_FIELD(ordNumCols);
+       WRITE_ATTRNUMBER_ARRAY(ordColIdx, node->ordNumCols);
+       WRITE_OID_ARRAY(ordOperators, node->ordNumCols);
+       WRITE_OID_ARRAY(ordCollations, node->ordNumCols);
+       WRITE_BOOL_ARRAY(ordNullsFirst, node->ordNumCols);
+       WRITE_INT_FIELD(frameOptions);
+       WRITE_NODE_FIELD(startOffset);
+       WRITE_NODE_FIELD(endOffset);
+       WRITE_OID_FIELD(startInRangeFunc);
+       WRITE_OID_FIELD(endInRangeFunc);
+       WRITE_OID_FIELD(inRangeColl);
+       WRITE_BOOL_FIELD(inRangeAsc);
+       WRITE_BOOL_FIELD(inRangeNullsFirst);
+}
+
 static void
 _outGroup(StringInfo str, const Group *node)
 {
@@ -4347,6 +4374,9 @@ outNode(StringInfo str, const void *obj)
                        case T_WindowAgg:
                                _outWindowAgg(str, obj);
                                break;
+                       case T_WindowHashAgg:
+                               _outWindowHashAgg(str, obj);
+                               break;
                        case T_Group:
                                _outGroup(str, obj);
                                break;
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index c5223413071..3d0bd4b1533 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -564,6 +564,8 @@ plannode_type(Plan *p)
                        return "TupleSplit";
                case T_WindowAgg:
                        return "WINDOWAGG";
+               case T_WindowHashAgg:
+                       return "WINDOWHASHAGG";
                case T_TableFunctionScan:
                        return "TABLEFUNCTIONSCAN";
                case T_Unique:
diff --git a/src/backend/nodes/readfast.c b/src/backend/nodes/readfast.c
index 887f9eae433..0cf853e5847 100644
--- a/src/backend/nodes/readfast.c
+++ b/src/backend/nodes/readfast.c
@@ -2076,6 +2076,9 @@ readNodeBinary(void)
                        case T_WindowAgg:
                                return_value = _readWindowAgg();
                                break;
+                       case T_WindowHashAgg:
+                               return_value = _readWindowHashAgg();
+                               break;
                        case T_TableFunctionScan:
                                return_value = _readTableFunctionScan();
                                break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 636e79d1cf4..cfe4c8d1d01 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2534,6 +2534,35 @@ _readWindowAgg(void)
        READ_DONE();
 }
 
+static WindowHashAgg *
+_readWindowHashAgg(void)
+{
+       READ_LOCALS(WindowHashAgg);
+
+       ReadCommonPlan(&local_node->plan);
+
+       READ_UINT_FIELD(winref);
+       READ_INT_FIELD(partNumCols);
+       READ_ATTRNUMBER_ARRAY(partColIdx, local_node->partNumCols);
+       READ_OID_ARRAY(partOperators, local_node->partNumCols);
+       READ_OID_ARRAY(partCollations, local_node->partNumCols);
+       READ_INT_FIELD(ordNumCols);
+       READ_ATTRNUMBER_ARRAY(ordColIdx, local_node->ordNumCols);
+       READ_OID_ARRAY(ordOperators, local_node->ordNumCols);
+       READ_OID_ARRAY(ordCollations, local_node->ordNumCols);
+       READ_BOOL_ARRAY(ordNullsFirst, local_node->ordNumCols);
+       READ_INT_FIELD(frameOptions);
+       READ_NODE_FIELD(startOffset);
+       READ_NODE_FIELD(endOffset);
+       READ_OID_FIELD(startInRangeFunc);
+       READ_OID_FIELD(endInRangeFunc);
+       READ_OID_FIELD(inRangeColl);
+       READ_BOOL_FIELD(inRangeAsc);
+       READ_BOOL_FIELD(inRangeNullsFirst);
+
+       READ_DONE();
+}
+
 /*
  * _readUnique
  */
@@ -3165,6 +3194,8 @@ parseNodeString(void)
                return_value = _readDQAExpr();
        else if (MATCH("WINDOWAGG", 9))
                return_value = _readWindowAgg();
+       else if (MATCH("WINDOWHASHAGG", 13))
+               return_value = _readWindowHashAgg();
        else if (MATCH("UNIQUE", 6))
                return_value = _readUnique();
        else if (MATCH("GATHER", 6))
diff --git a/src/backend/optimizer/plan/orca.c 
b/src/backend/optimizer/plan/orca.c
index 0d706129d58..dc6a88e73c6 100644
--- a/src/backend/optimizer/plan/orca.c
+++ b/src/backend/optimizer/plan/orca.c
@@ -41,7 +41,7 @@
 #include "utils/lsyscache.h"
 
 /* GPORCA entry point */
-extern PlannedStmt * GPOPTOptimizedPlan(Query *parse, bool 
*had_unexpected_failure);
+extern PlannedStmt * GPOPTOptimizedPlan(Query *parse, bool 
*had_unexpected_failure, OptimizerOptions *opts);
 
 static Plan *remove_redundant_results(PlannerInfo *root, Plan *plan);
 static Node *remove_redundant_results_mutator(Node *node, void *);
@@ -89,7 +89,7 @@ log_optimizer(PlannedStmt *plan, bool fUnexpectedFailure)
  * This is the main entrypoint for invoking Orca.
  */
 PlannedStmt *
-optimize_query(Query *parse, int cursorOptions, ParamListInfo boundParams)
+optimize_query(Query *parse, int cursorOptions, ParamListInfo boundParams, 
OptimizerOptions *options)
 {
        /* flag to check if optimizer unexpectedly failed to produce a plan */
        bool                    fUnexpectedFailure = false;
@@ -153,7 +153,7 @@ optimize_query(Query *parse, int cursorOptions, 
ParamListInfo boundParams)
        pqueryCopy = (Query *) transformGroupedWindows((Node *) pqueryCopy, 
NULL);
 
        /* Ok, invoke ORCA. */
-       result = GPOPTOptimizedPlan(pqueryCopy, &fUnexpectedFailure);
+       result = GPOPTOptimizedPlan(pqueryCopy, &fUnexpectedFailure, options);
 
        log_optimizer(result, fUnexpectedFailure);
 
diff --git a/src/backend/optimizer/plan/planner.c 
b/src/backend/optimizer/plan/planner.c
index 22ec8b43266..e4b25e97b33 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -328,13 +328,16 @@ planner(Query *parse, const char *query_string, int 
cursorOptions,
 {
        PlannedStmt *result;
        instr_time      starttime, endtime;
+       OptimizerOptions *optimizer_options;
 
+       optimizer_options = palloc(sizeof(OptimizerOptions));
+       optimizer_options->create_vectorization_plan = false;
        if (planner_hook)
        {
                if (gp_log_optimization_time)
                        INSTR_TIME_SET_CURRENT(starttime);
 
-               result = (*planner_hook) (parse, query_string, cursorOptions, 
boundParams);
+               result = (*planner_hook) (parse, query_string, cursorOptions, 
boundParams, optimizer_options);
 
                if (gp_log_optimization_time)
                {
@@ -344,14 +347,14 @@ planner(Query *parse, const char *query_string, int 
cursorOptions,
                }
        }
        else
-               result = standard_planner(parse, query_string, cursorOptions, 
boundParams);
-
+               result = standard_planner(parse, query_string, cursorOptions, 
boundParams, optimizer_options);
+       pfree(optimizer_options);
        return result;
 }
 
 PlannedStmt *
 standard_planner(Query *parse, const char *query_string, int cursorOptions,
-                                ParamListInfo boundParams)
+                                ParamListInfo boundParams, OptimizerOptions 
*optimizer_options)
 {
        PlannedStmt *result;
        PlannerGlobal *glob;
@@ -404,7 +407,7 @@ standard_planner(Query *parse, const char *query_string, 
int cursorOptions,
                        INSTR_TIME_SET_CURRENT(starttime);
 
 #ifdef USE_ORCA
-               result = optimize_query(parse, cursorOptions, boundParams);
+               result = optimize_query(parse, cursorOptions, boundParams, 
optimizer_options);
 #else
                /* Make sure this branch is not taken in builds using 
--disable-orca. */
                Assert(false);
diff --git a/src/backend/optimizer/util/walkers.c 
b/src/backend/optimizer/util/walkers.c
index 93834cb32f7..3b3d0311d06 100644
--- a/src/backend/optimizer/util/walkers.c
+++ b/src/backend/optimizer/util/walkers.c
@@ -403,6 +403,14 @@ plan_tree_walker(Node *node,
                        if (walker(((WindowAgg *) node)->endOffset, context))
                                return true;
                        break;
+               case T_WindowHashAgg:
+                       if (walk_plan_node_fields((Plan *) node, walker, 
context))
+                               return true;
+                       if (walker(((WindowHashAgg *) node)->startOffset, 
context))
+                               return true;
+                       if (walker(((WindowHashAgg *) node)->endOffset, 
context))
+                               return true;
+                       break;
 
                case T_Unique:
                        if (walk_plan_node_fields((Plan *) node, walker, 
context))
diff --git a/src/backend/utils/misc/guc_gp.c b/src/backend/utils/misc/guc_gp.c
index df83e03b766..02de621cb54 100644
--- a/src/backend/utils/misc/guc_gp.c
+++ b/src/backend/utils/misc/guc_gp.c
@@ -365,6 +365,7 @@ bool                optimizer_enable_replicated_table;
 bool           optimizer_enable_foreign_table;
 bool           optimizer_enable_right_outer_join;
 bool           optimizer_enable_query_parameter;
+bool           optimizer_force_window_hash_agg;
 
 /* Optimizer plan enumeration related GUCs */
 bool           optimizer_enumerate_plans;
@@ -3288,6 +3289,17 @@ struct config_bool ConfigureNamesBool_gp[] =
                true,
                NULL, NULL, NULL
        },
+       {
+               {"optimizer_force_window_hash_agg", PGC_USERSET, 
DEVELOPER_OPTIONS,
+                gettext_noop("Enable create window hash agg."),
+                NULL,
+                GUC_NOT_IN_SAMPLE
+               },
+               &optimizer_force_window_hash_agg, // TODO: remove it before 
merge
+               false,
+               NULL, NULL, NULL
+       },
+
        {
                {"aqumv_allow_foreign_table", PGC_USERSET, DEVELOPER_OPTIONS,
                        gettext_noop("allow answer query using materialized 
views which have foreign or external tables."),
diff --git a/src/backend/utils/resource_manager/memquota.c 
b/src/backend/utils/resource_manager/memquota.c
index f1e272e9f24..1cf5f805df9 100644
--- a/src/backend/utils/resource_manager/memquota.c
+++ b/src/backend/utils/resource_manager/memquota.c
@@ -247,6 +247,7 @@ IsMemoryIntensiveOperator(Node *node, PlannedStmt *stmt)
                case T_Hash:
                case T_BitmapIndexScan:
                case T_WindowAgg:
+               case T_WindowHashAgg:
                case T_TableFunctionScan:
                case T_FunctionScan:
                        return true;
diff --git a/src/include/gpopt/CGPOptimizer.h b/src/include/gpopt/CGPOptimizer.h
index 243e42c6da1..4a191a8009a 100644
--- a/src/include/gpopt/CGPOptimizer.h
+++ b/src/include/gpopt/CGPOptimizer.h
@@ -18,6 +18,7 @@
 extern "C" {
 #include "postgres.h"
 
+#include "optimizer/orcaopt.h"
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
@@ -30,7 +31,8 @@ public:
        static PlannedStmt *GPOPTOptimizedPlan(
                Query *query,
                bool *
-                       had_unexpected_failure  // output : set to true if 
optimizer unexpectedly failed to produce plan
+                       had_unexpected_failure, // output : set to true if 
optimizer unexpectedly failed to produce plan
+               OptimizerOptions *opts
        );
 
        // serialize planned statement into DXL
@@ -45,7 +47,8 @@ public:
 extern "C" {
 
 extern PlannedStmt *GPOPTOptimizedPlan(Query *query,
-                                                                          bool 
*had_unexpected_failure);
+                                                                          bool 
*had_unexpected_failure,
+                                                                          
OptimizerOptions *opts);
 extern char *SerializeDXLPlan(Query *query);
 extern void InitGPOPT();
 extern void TerminateGPOPT();
diff --git a/src/include/gpopt/config/CConfigParamMapping.h 
b/src/include/gpopt/config/CConfigParamMapping.h
index ec314640b9d..32d81825384 100644
--- a/src/include/gpopt/config/CConfigParamMapping.h
+++ b/src/include/gpopt/config/CConfigParamMapping.h
@@ -68,7 +68,7 @@ public:
        CConfigParamMapping(const CConfigParamMapping &) = delete;
 
        // pack enabled optimizer config params in a traceflag bitset
-       static CBitSet *PackConfigParamInBitset(CMemoryPool *mp, ULONG 
xform_id);
+       static CBitSet *PackConfigParamInBitset(CMemoryPool *mp, ULONG 
xform_id, BOOL create_vec_plan);
 };
 }  // namespace gpdxl
 
diff --git a/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h 
b/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h
index b2cb6104fb2..625ed5cd0a3 100644
--- a/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h
+++ b/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h
@@ -291,12 +291,19 @@ private:
        );
 
        // translate DXL window node into GPDB window node
-       Plan *TranslateDXLWindow(
+       Plan *TranslateDXLWindowAgg(
                const CDXLNode *motion_dxlnode, CDXLTranslateContext 
*output_context,
                CDXLTranslationContextArray *
                        ctxt_translation_prev_siblings  // translation contexts 
of previous siblings
        );
 
+       // translate DXL window node into window hash agg node
+       Plan *TranslateDXLWindowHashAgg(
+               const CDXLNode *window_dxlnode, CDXLTranslateContext 
*output_context,
+               CDXLTranslationContextArray 
+                       *ctxt_translation_prev_siblings // translation contexts 
of previous siblings
+       );
+
        // translate DXL sort node into GPDB Sort plan node
        Plan *TranslateDXLSort(
                const CDXLNode *sort_dxlnode, CDXLTranslateContext 
*output_context,
diff --git a/src/include/gpopt/utils/COptTasks.h 
b/src/include/gpopt/utils/COptTasks.h
index 2de78c7667a..3fa5f91216c 100644
--- a/src/include/gpopt/utils/COptTasks.h
+++ b/src/include/gpopt/utils/COptTasks.h
@@ -15,6 +15,10 @@
 #ifndef COptTasks_H
 #define COptTasks_H
 
+extern "C" {
+#include "optimizer/orcaopt.h"
+}
+
 #include "gpos/error/CException.h"
 
 #include "gpopt/base/CColRef.h"
@@ -108,6 +112,9 @@ struct SOptContext
        // casting function
        static SOptContext *Cast(void *ptr);
 
+       // other options pass by OptimizerOptions
+       BOOL m_create_vec_plan;
+
 };     // struct SOptContext
 
 class COptTasks
@@ -160,7 +167,8 @@ public:
 
        // optimize Query->DXL->LExpr->Optimize->PExpr->DXL->PlannedStmt
        static PlannedStmt *GPOPTOptimizedPlan(Query *query,
-                                                                               
   SOptContext *gpopt_context);
+                                                                               
   SOptContext *gpopt_context,
+                                                                               
   OptimizerOptions *opts);
 
        // enable/disable a given xforms
        static bool SetXform(char *xform_str, bool should_disable);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 2ec47618998..30c0c09c6e4 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -103,6 +103,7 @@ typedef enum NodeTag
        T_Agg,
        T_TupleSplit,
        T_WindowAgg,
+       T_WindowHashAgg,
        T_Unique,
        T_Gather,
        T_GatherMerge,
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index d2b2d921fba..e4695fd56f2 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -1442,6 +1442,47 @@ typedef struct WindowAgg
        bool            inRangeNullsFirst;      /* nulls sort first for 
in_range tests? */
 } WindowAgg;
 
+
+/* ----------------
+ *             window hash aggregate node
+ *
+ * A WindowHashAgg node implements window functions over zero or more
+ * ordering/framing specifications within a partition specification on
+ * appropriately UNORDERED input.
+ * 
+ * CBDB does not create plans with `WindowHashAgg` for the row executor,
+ * but does for the vectorization executor.
+ * ----------------
+ */
+typedef struct WindowHashAgg
+{
+       Plan            plan;
+       Index           winref;                 /* ID referenced by window 
functions */
+       int                     partNumCols;    /* number of columns in 
partition clause */
+       AttrNumber *partColIdx;         /* their indexes in the target list */
+       Oid                *partOperators;      /* equality operators for 
partition columns */
+       Oid                *partCollations; /* collations for partition columns 
*/
+       /*
+        * Different with `WindowAgg`, WindowHashAgg may use the 
+        * `order by` information.
+        */
+       int                     ordNumCols;             /* number of sort-key 
columns */
+       AttrNumber *ordColIdx;          /* their indexes in the target list */
+       Oid                *ordOperators;       /* OIDs of operators to sort 
them by */
+       Oid                *ordCollations;      /* OIDs of collations */
+       bool       *ordNullsFirst;      /* NULLS FIRST/LAST directions */
+
+       int                     frameOptions;   /* frame_clause options, see 
WindowDef */
+       Node       *startOffset;        /* expression for starting bound, if 
any */
+       Node       *endOffset;          /* expression for ending bound, if any 
*/
+       /* these fields are used with RANGE offset PRECEDING/FOLLOWING: */
+       Oid                     startInRangeFunc;       /* in_range function 
for startOffset */
+       Oid                     endInRangeFunc; /* in_range function for 
endOffset */
+       Oid                     inRangeColl;    /* collation for in_range tests 
*/
+       bool            inRangeAsc;             /* use ASC sort order for 
in_range tests? */
+       bool            inRangeNullsFirst;      /* nulls sort first for 
in_range tests? */
+} WindowHashAgg;
+
 /* ----------------
  *             unique node
  * ----------------
diff --git a/src/include/optimizer/orca.h b/src/include/optimizer/orca.h
index d26903fc085..a32af3f56fd 100644
--- a/src/include/optimizer/orca.h
+++ b/src/include/optimizer/orca.h
@@ -18,10 +18,11 @@
 #define ORCA_H
 
 #include "pg_config.h"
+#include "optimizer/orcaopt.h"
 
 #ifdef USE_ORCA
 
-extern PlannedStmt * optimize_query(Query *parse, int cursorOptions, 
ParamListInfo boundParams);
+extern PlannedStmt * optimize_query(Query *parse, int cursorOptions, 
ParamListInfo boundParams, OptimizerOptions *options);
 extern Node *transformGroupedWindows(Node *node, void *context);
 
 // plan_hint_hook generates HintState by parsing a Query.
diff --git a/src/include/optimizer/orcaopt.h b/src/include/optimizer/orcaopt.h
new file mode 100644
index 00000000000..2bb5395875c
--- /dev/null
+++ b/src/include/optimizer/orcaopt.h
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * orcaopt.h
+ *
+ * IDENTIFICATION
+ *       src/include/optimizer/orcaopt.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ORCA_OPT_H
+#define ORCA_OPT_H
+
+#include "pg_config.h"
+
+#ifdef USE_ORCA
+
+typedef struct OptimizerOptions 
+{
+    bool create_vectorization_plan;
+} OptimizerOptions;
+
+#endif
+
+#endif /* ORCA_H */
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index 29aac021f38..610034b2c62 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -21,13 +21,14 @@
 #include "nodes/pathnodes.h"
 #include "nodes/plannerconfig.h"
 #include "nodes/plannodes.h"
-
+#include "optimizer/orcaopt.h"
 
 /* Hook for plugins to get control in planner() */
 typedef PlannedStmt *(*planner_hook_type) (Query *parse,
                                                                                
   const char *query_string,
                                                                                
   int cursorOptions,
-                                                                               
   ParamListInfo boundParams);
+                                                                               
   ParamListInfo boundParams,
+                                                                               
   OptimizerOptions *optimizer_options);
 extern PGDLLIMPORT planner_hook_type planner_hook;
 
 /* Hook for plugins to get control when grouping_planner() plans upper rels */
@@ -41,7 +42,8 @@ extern PGDLLIMPORT create_upper_paths_hook_type 
create_upper_paths_hook;
 
 extern PlannedStmt *standard_planner(Query *parse, const char *query_string,
                                                                         int 
cursorOptions,
-                                                                        
ParamListInfo boundParams);
+                                                                        
ParamListInfo boundParams,
+                                                                        
OptimizerOptions *optimizer_options);
 
 extern PlannerInfo *subquery_planner(PlannerGlobal *glob, Query *parse,
                                                                         
PlannerInfo *parent_root,
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index de0f931c797..40838ba969d 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -546,6 +546,7 @@ extern bool optimizer_enable_replicated_table;
 extern bool optimizer_enable_foreign_table;
 extern bool optimizer_enable_right_outer_join;
 extern bool optimizer_enable_query_parameter;
+extern bool optimizer_force_window_hash_agg;
 
 /* Optimizer plan enumeration related GUCs */
 extern bool optimizer_enumerate_plans;
diff --git a/src/include/utils/unsync_guc_name.h 
b/src/include/utils/unsync_guc_name.h
index a2a325bd9ed..b3a19ca7b9f 100644
--- a/src/include/utils/unsync_guc_name.h
+++ b/src/include/utils/unsync_guc_name.h
@@ -440,6 +440,7 @@
                "optimizer_enable_foreign_table",
                "optimizer_enable_right_outer_join",
                "optimizer_enable_query_parameter",
+               "optimizer_force_window_hash_agg",
                "optimizer_enforce_subplans",
                "optimizer_enumerate_plans",
                "optimizer_expand_fulljoin",
diff --git a/src/test/modules/delay_execution/delay_execution.c 
b/src/test/modules/delay_execution/delay_execution.c
index b3d0841ba80..03925c74880 100644
--- a/src/test/modules/delay_execution/delay_execution.c
+++ b/src/test/modules/delay_execution/delay_execution.c
@@ -44,17 +44,17 @@ void                _PG_fini(void);
 /* planner_hook function to provide the desired delay */
 static PlannedStmt *
 delay_execution_planner(Query *parse, const char *query_string,
-                                               int cursorOptions, 
ParamListInfo boundParams)
+                                               int cursorOptions, 
ParamListInfo boundParams, OptimizerOptions *optimizer_options)
 {
        PlannedStmt *result;
 
        /* Invoke the planner, possibly via a previous hook user */
        if (prev_planner_hook)
                result = prev_planner_hook(parse, query_string, cursorOptions,
-                                                                  boundParams);
+                                                                  boundParams, 
optimizer_options);
        else
                result = standard_planner(parse, query_string, cursorOptions,
-                                                                 boundParams);
+                                                                 boundParams, 
optimizer_options);
 
        /* If enabled, delay by taking and releasing the specified lock */
        if (post_planning_lock_id != 0)
diff --git a/src/test/regress/hooktest/hook_test.c 
b/src/test/regress/hooktest/hook_test.c
index bf0e6d5f5f0..d06d0fe1c44 100644
--- a/src/test/regress/hooktest/hook_test.c
+++ b/src/test/regress/hooktest/hook_test.c
@@ -15,7 +15,7 @@ PG_MODULE_MAGIC;
 
 static planner_hook_type prev_planner_hook = NULL;
 
-static PlannedStmt *test_planner_hook(Query *parse, const char *query_string, 
int cursorOptions, ParamListInfo boundParams);
+static PlannedStmt *test_planner_hook(Query *parse, const char *query_string, 
int cursorOptions, ParamListInfo boundParams, OptimizerOptions 
*optimizer_options);
 
 void           _PG_init(void);
 void           _PG_fini(void);
@@ -34,16 +34,16 @@ _PG_fini(void)
 }
 
 static PlannedStmt *
-test_planner_hook(Query *parse, const char *query_string, int cursorOptions, 
ParamListInfo boundParams)
+test_planner_hook(Query *parse, const char *query_string, int cursorOptions, 
ParamListInfo boundParams, OptimizerOptions *optimizer_options)
 {
        PlannedStmt *stmt;
 
        elog(LOG, "In test_planner_hook");
 
        if (prev_planner_hook)
-               stmt = (*prev_planner_hook) (parse, query_string, 
cursorOptions, boundParams);
+               stmt = (*prev_planner_hook) (parse, query_string, 
cursorOptions, boundParams, optimizer_options);
        else
-               stmt = standard_planner(parse, query_string, cursorOptions, 
boundParams);
+               stmt = standard_planner(parse, query_string, cursorOptions, 
boundParams, optimizer_options);
 
        return stmt;
 }
diff --git a/src/test/unit/mock/gpopt_mock.c b/src/test/unit/mock/gpopt_mock.c
index 39bf2c49e82..ae16a4cab75 100644
--- a/src/test/unit/mock/gpopt_mock.c
+++ b/src/test/unit/mock/gpopt_mock.c
@@ -3,6 +3,7 @@
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
+#include "optimizer/orcaopt.h"
 
 char *
 SerializeDXLPlan(Query *pquery)
@@ -12,7 +13,7 @@ SerializeDXLPlan(Query *pquery)
 }
 
 PlannedStmt *
-GPOPTOptimizedPlan(Query *pquery, bool pfUnexpectedFailure)
+GPOPTOptimizedPlan(Query *pquery, bool pfUnexpectedFailure, OptimizerOptions 
*opts)
 {
        elog(ERROR, "mock implementation of GPOPTOptimizedPlan called");
        return NULL;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to