//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2012 EMC Corp.
//
//	@filename:
//		CQueryMutators.h
//
//	@doc:
//		Class providing methods for translating a GPDB Query object into a
//		DXL Tree
//
//	@test:
//
//
//---------------------------------------------------------------------------

#ifndef GPDXL_CWalkerUtils_H
#define GPDXL_CWalkerUtils_H

#include "gpos/base.h"

#include "gpopt/translate/CMappingVarColId.h"
#include "gpopt/translate/CTranslatorScalarToDXL.h"
#include "gpopt/translate/CTranslatorUtils.h"
#include "naucrates/dxl/operators/CDXLNode.h"
#include "naucrates/md/IMDType.h"

// fwd declarations
namespace gpopt
{
class CMDAccessor;
}

struct Query;
struct RangeTblEntry;
struct Const;
struct List;


namespace gpdxl
{
//---------------------------------------------------------------------------
//	@class:
//		CQueryMutators
//
//	@doc:
//		Class providing methods for translating a GPDB Query object into a
//      DXL Tree.
//
//---------------------------------------------------------------------------
class CQueryMutators
{
	using MutatorWalkerFn = Node *(*)(Node*, void*);
	using ExprWalkerFn = BOOL (*)(Node*, void*);

	using CContextGrpbyPlMutator = struct SContextGrpbyPlMutator
	{
	public:
		// memory pool
		CMemoryPool *m_mp;

		// MD accessor to get the function name
		CMDAccessor *m_mda;

		// original query
		Query *m_query;

		// the new target list of the group by or window (lower) query
		List *m_lower_table_tlist;

		// the current query level
		ULONG m_current_query_level;

		// indicate the levels up of the aggregate we are mutating
		ULONG m_agg_levels_up;

		// indicate whether we are mutating the argument of an aggregate
		BOOL m_is_mutating_agg_arg;

		// ctor
		SContextGrpbyPlMutator(CMemoryPool *mp, CMDAccessor *mda, Query *query,
							   List *derived_table_tlist)
			: m_mp(mp),
			  m_mda(mda),
			  m_query(query),
			  m_lower_table_tlist(derived_table_tlist),
			  m_current_query_level(0),
			  m_agg_levels_up(gpos::ulong_max),
			  m_is_mutating_agg_arg(false)
		{
		}

		// dtor
		~SContextGrpbyPlMutator() = default;
	};

	using CContextIncLevelsupMutator = struct SContextIncLevelsupMutator
	{
	public:
		// the current query level
		ULONG m_current_query_level;

		// fix target list entry of the top level
		BOOL m_should_fix_top_level_target_list;

		// ctor
		SContextIncLevelsupMutator(ULONG current_query_level,
								   BOOL should_fix_top_level_target_list)
			: m_current_query_level(current_query_level),
			  m_should_fix_top_level_target_list(
				  should_fix_top_level_target_list)
		{
		}

		// dtor
		~SContextIncLevelsupMutator() = default;
	};

	// context for walker that iterates over the expression in the target entry
	using CContextTLWalker = struct SContextTLWalker
	{
	public:
		// list of target list entries in the query
		List *m_target_entries;

		// list of grouping clauses
		List *m_group_clause;

		// ctor
		SContextTLWalker(List *target_entries, List *group_clause)
			: m_target_entries(target_entries), m_group_clause(group_clause)
		{
		}

		// dtor
		~SContextTLWalker() = default;
	};

public:
	// fall back during since the target list refers to a attribute which algebrizer at this point cannot resolve
	static BOOL ShouldFallback(Node *node, SContextTLWalker *context);

	// check if the project list contains expressions on aggregates thereby needing normalization
	static BOOL NeedsProjListNormalization(const Query *query);

	// normalize query
	static Query *NormalizeQuery(CMemoryPool *mp, CMDAccessor *md_accessor,
								 const Query *query, ULONG query_level);

	// check if the project list contains expressions on window operators thereby needing normalization
	static BOOL NeedsProjListWindowNormalization(const Query *query);

	// flatten expressions in window operation project list
	static Query *NormalizeWindowProjList(CMemoryPool *mp,
										  CMDAccessor *md_accessor,
										  const Query *original_query);

	// traverse the project list to extract all window functions in an arbitrarily complex project element
	static Node *RunWindowProjListMutator(Node *node,
										  SContextGrpbyPlMutator *context);

	// flatten expressions in project list
	static Query *NormalizeGroupByProjList(CMemoryPool *mp,
										   CMDAccessor *md_accessor,
										   const Query *query);

	// make a copy of the aggref (minus the arguments)
	static Aggref *FlatCopyAggref(Aggref *aggref);

	// create a new entry in the derived table and return its corresponding var
	static Var *MakeVarInDerivedTable(Node *node,
									  SContextGrpbyPlMutator *context);

	// check if a matching node exists in the list of target entries
	static Node *FindNodeInGroupByTargetList(Node *node,
											 SContextGrpbyPlMutator *context);

	// increment the levels up of outer references
	static Var *IncrLevelsUpIfOuterRef(Var *var);

	// pull up having clause into a select
	static Query *NormalizeHaving(CMemoryPool *mp, CMDAccessor *md_accessor,
								  const Query *query);

	// traverse the expression and fix the levels up of any outer reference
	static Node *RunIncrLevelsUpMutator(Node *node,
										SContextIncLevelsupMutator *context);

	// traverse the expression and fix the levels up of any CTE
	static BOOL RunFixCTELevelsUpWalker(Node *node,
										SContextIncLevelsupMutator *context);

	// mutate the grouping columns, fix levels up when necessary
	static Node *RunGroupingColMutator(Node *node,
									   SContextGrpbyPlMutator *context);

	// fix the level up of grouping columns when necessary
	static Node *FixGroupingCols(Node *node, TargetEntry *original,
								 SContextGrpbyPlMutator *context);

	// return a target entry for the aggregate expression
	static TargetEntry *GetTargetEntryForAggExpr(CMemoryPool *mp,
												 CMDAccessor *md_accessor,
												 Node *node, ULONG attno);

	// traverse the having qual to extract all aggregate functions,
	// fix correlated vars and return the modified having qual
	static Node *RunExtractAggregatesMutator(Node *node,
											 SContextGrpbyPlMutator *context);

	// for a given an TE in the derived table, create a new TE to be added to the top level query
	static TargetEntry *MakeTopLevelTargetEntry(TargetEntry *target_entry,
												ULONG attno);

	// return the column name of the target entry
	static CHAR *GetTargetEntryColName(TargetEntry *target_entry, Query *query);

	// make the input query into a derived table and return a new root query
	static Query *ConvertToDerivedTable(const Query *original_query,
										BOOL should_fix_target_list,
										BOOL should_fix_having_qual);

	// eliminate distinct clause
	static Query *EliminateDistinctClause(const Query *query);

	// reassign the sorting clause from the derived table to the new top-level query
	static void ReassignSortClause(Query *top_level_query,
								   Query *derive_table_query);
};
}  // namespace gpdxl
#endif	// GPDXL_CWalkerUtils_H

//EOF
