//---------------------------------------------------------------------------
//	@function:
//		CXformCollapseGbAgg::Transform
//
//	@doc:
//		Actual transformation to collapse two cascaded group by operators;
//		if the top Gb grouping columns are subset of bottom Gb grouping
//		columns AND both Gb operators do not define agg functions, we can
//		remove the bottom group by operator
//
//
//---------------------------------------------------------------------------
void
CXformCollapseGbAgg::Transform
	(
	CXformContext *pxfctxt,
	CXformResult *pxfres,
	CExpression *pexpr
	)
	const
{
	GPOS_ASSERT(NULL != pxfctxt);
	GPOS_ASSERT(NULL != pxfres);
	GPOS_ASSERT(FPromising(pxfctxt->Pmp(), this, pexpr));
	GPOS_ASSERT(FCheckPattern(pexpr));

	IMemoryPool *pmp = pxfctxt->Pmp();

	// extract components
	CLogicalGbAgg *popTopGbAgg = CLogicalGbAgg::PopConvert(pexpr->Pop());
	GPOS_ASSERT(0 < popTopGbAgg->Pdrgpcr()->UlLength());
	GPOS_ASSERT(popTopGbAgg->FGlobal());

	CExpression *pexprRelational = (*pexpr)[0];
	CExpression *pexprTopProjectList = (*pexpr)[1];

	CLogicalGbAgg *popBottomGbAgg = CLogicalGbAgg::PopConvert(pexprRelational->Pop());
	CExpression *pexprChild = (*pexprRelational)[0];
	CExpression *pexprBottomProjectList = (*pexprRelational)[1];

	if (!popBottomGbAgg->FGlobal())
	{
		// bottom GbAgg must be global to prevent xform from getting applied to splitted GbAggs
		return;
	}

	if (0 < pexprTopProjectList->UlArity() || 0 < pexprBottomProjectList->UlArity())
	{
		// exit if any of the Gb operators has an aggregate function
		return;
	}

#ifdef GPOS_DEBUG
	// for two cascaded GbAgg ops with no agg functions, top grouping
	// columns must be a subset of bottom grouping columns
	CColRefSet *pcrsTopGrpCols = GPOS_NEW(pmp) CColRefSet(pmp, popTopGbAgg->Pdrgpcr());
	CColRefSet *pcrsBottomGrpCols = GPOS_NEW(pmp) CColRefSet(pmp, popBottomGbAgg->Pdrgpcr());
	GPOS_ASSERT(pcrsBottomGrpCols->FSubset(pcrsTopGrpCols));

	pcrsTopGrpCols->Release();
	pcrsBottomGrpCols->Release();
#endif // GPOS_DEBUG

	pexprChild->AddRef();
	CExpression *pexprSelect = CUtils::PexprLogicalSelect(pmp, pexprChild, CPredicateUtils::PexprConjunction(pmp, NULL /*pdrgpexpr*/));

	popTopGbAgg->AddRef();
	pexprTopProjectList->AddRef();
	CExpression *pexprGbAggNew = GPOS_NEW(pmp) CExpression(pmp, popTopGbAgg, pexprSelect, pexprTopProjectList);

	pxfres->Add(pexprGbAggNew);
}
//---------------------------------------------------------------------------
//	@function:
//		CXformPushGbWithHavingBelowJoin::Transform
//
//	@doc:
//		Actual transformation
//
//---------------------------------------------------------------------------
void
CXformPushGbWithHavingBelowJoin::Transform
	(
	CXformContext *pxfctxt,
	CXformResult *pxfres,
	CExpression *pexpr
	)
	const
{
	GPOS_ASSERT(NULL != pxfctxt);
	GPOS_ASSERT(FPromising(pxfctxt->Pmp(), this, pexpr));
	GPOS_ASSERT(FCheckPattern(pexpr));

	IMemoryPool *mp = pxfctxt->Pmp();

	CExpression *pexprGb = (*pexpr)[0];
	CLogicalGbAgg *popGbAgg = CLogicalGbAgg::PopConvert(pexprGb->Pop());
	if (!popGbAgg->FGlobal())
	{
		// xform only applies to global aggs
		return;
	}

	CExpression *pexprResult = CXformUtils::PexprPushGbBelowJoin(mp, pexpr);

	if (NULL != pexprResult)
	{
		// add alternative to results
		pxfres->Add(pexprResult);
	}
}
//---------------------------------------------------------------------------
//	@function:
//		CXformCollapseGbAgg::Exfp
//
//	@doc:
//		Compute xform promise for a given expression handle;
//		GbAgg must be global and have non empty grouping columns
//
//---------------------------------------------------------------------------
CXform::EXformPromise
CXformCollapseGbAgg::Exfp
	(
	CExpressionHandle &exprhdl
	)
	const
{
	CLogicalGbAgg *popAgg = CLogicalGbAgg::PopConvert(exprhdl.Pop());
	if (!popAgg->FGlobal() || 0 == popAgg->Pdrgpcr()->UlLength())
	{
		return CXform::ExfpNone;
	}

	return CXform::ExfpHigh;
}
//---------------------------------------------------------------------------
//	@function:
//		CXformPushGbBelowJoin::Exfp
//
//	@doc:
//		Compute xform promise for a given expression handle;
//		we only push down global aggregates
//
//---------------------------------------------------------------------------
CXform::EXformPromise
CXformPushGbBelowJoin::Exfp
	(
	CExpressionHandle &exprhdl
	)
	const
{
	CLogicalGbAgg *popGbAgg = CLogicalGbAgg::PopConvert(exprhdl.Pop());
	if (!popGbAgg->FGlobal())
	{
		return CXform::ExfpNone;
	}

	return CXform::ExfpHigh;
}
//---------------------------------------------------------------------------
//	@function:
//		CXformGbAgg2Apply::Exfp
//
//	@doc:
//		Compute xform promise for a given expression handle;
//		scalar child must have subquery
//
//---------------------------------------------------------------------------
CXform::EXformPromise
CXformGbAgg2Apply::Exfp
	(
	CExpressionHandle &exprhdl
	)
	const
{
	CLogicalGbAgg *popGbAgg = CLogicalGbAgg::PopConvert(exprhdl.Pop());
	if (popGbAgg->FGlobal() && exprhdl.Pdpscalar(1)->FHasSubquery())
	{
		return CXform::ExfpHigh;
	}

	return CXform::ExfpNone;
}