//---------------------------------------------------------------------------

//  Greenplum Database
//  Copyright (C) 2018 VMware, Inc. or its affiliates.
//
//  @filename:
//      CXformEagerAgg.h
//
//  @doc:
//      Eagerly push aggregates below join when there is no primary/foreign keys
//---------------------------------------------------------------------------
#ifndef GPOPT_CXformEagerAgg_H
#define GPOPT_CXformEagerAgg_H

#include "gpos/base.h"

#include "gpopt/xforms/CXformExploration.h"

namespace gpopt
{
using namespace gpos;

//---------------------------------------------------------------------------
//	@class:
//		CXformEagerAgg
//
//	@doc:
//		Eagerly push aggregates below join
//
//---------------------------------------------------------------------------
class CXformEagerAgg : public CXformExploration
{
public:
	CXformEagerAgg(const CXformEagerAgg &) = delete;

	// ctor
	explicit CXformEagerAgg(CMemoryPool *mp);

	// ctor
	explicit CXformEagerAgg(CExpression *exprPattern);

	// dtor
	~CXformEagerAgg() override = default;

	// ident accessors
	EXformId
	Exfid() const override
	{
		return ExfEagerAgg;
	}

	const CHAR *
	SzId() const override
	{
		return "CXformEagerAgg";
	}

	// compatibility function for eager aggregation
	BOOL
	FCompatible(CXform::EXformId exfid) override
	{
		return (CXform::ExfEagerAgg != exfid) &&
			   (CXform::ExfSplitGbAgg != exfid) &&
			   (CXform::ExfSplitDQA != exfid);
	}

	// compute xform promise for a given expression handle
	EXformPromise Exfp(CExpressionHandle &exprhdl) const override;

	// actual transform
	void Transform(CXformContext *pxfctxt, CXformResult *pxfres,
				   CExpression *expr) const override;

	// return true if xform should be applied only once
	BOOL
	IsApplyOnce() override
	{
		return true;
	};

private:
	// check if transform can be applied
	static BOOL CanApplyTransform(CExpression *agg_expr);

	// is this aggregate supported for push down?
	static BOOL CanPushAggBelowJoin(CExpression *scalar_agg_func_expr);

	// generate project lists for the lower and upper aggregates
	// from all the original aggregates
	static void PopulateLowerUpperProjectList(
		CMemoryPool *mp,			  // memory pool
		CExpression *orig_proj_list,  // project list of the original aggregate
		CExpression *
			*lower_proj_list,  // output project list of the new lower aggregate
		CExpression *
			*upper_proj_list  // output project list of the new upper aggregate
	);

	// generate project element for lower aggregate for a single original aggregate
	static void PopulateLowerProjectElement(
		CMemoryPool *mp,  // memory pool
		IMDId *agg_mdid,  // original global aggregate function
		CWStringConst *agg_name, CExpressionArray *agg_arg_array,
		BOOL is_distinct, BOOL is_agg_star, ULongPtrArray *arg_types,
		CExpression **lower_proj_elem_expr	// output project element of the new
											// lower aggregate
	);

	// generate project element for upper aggregate
	static void PopulateUpperProjectElement(
		CMemoryPool *mp,  // memory pool
		IMDId *agg_mdid,  // aggregate mdid to create
		CWStringConst *agg_name, CColRef *lower_colref, CColRef *output_colref,
		BOOL is_distinct, BOOL is_agg_star, ULongPtrArray *arg_types,
		CExpression **upper_proj_elem_expr	// output project element of the new
											// upper aggregate
	);
};	// class CXformEagerAgg
}  // namespace gpopt

#endif	// !GPOPT_CXformEagerAgg_H

// EOF
