//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2012 EMC Corp.
//
//	@filename:
//		CTranslatorUtils.h
//
//	@doc:
//		Class providing utility methods for translating GPDB's PlannedStmt/Query
//		into DXL Tree
//
//	@test:
//
//---------------------------------------------------------------------------

#ifndef GPDXL_CTranslatorUtils_H
#define GPDXL_CTranslatorUtils_H
#define GPDXL_SYSTEM_COLUMNS 8

extern "C" {
#include "postgres.h"

#include "access/sdir.h"
#include "access/skey.h"
#include "nodes/parsenodes.h"
}

#include "gpos/base.h"
#include "gpos/common/CBitSet.h"

#include "gpopt/translate/CMappingVarColId.h"
#include "naucrates/dxl/CIdGenerator.h"
#include "naucrates/dxl/operators/CDXLIndexDescr.h"
#include "naucrates/dxl/operators/CDXLLogicalSetOp.h"
#include "naucrates/dxl/operators/CDXLLogicalTVF.h"
#include "naucrates/dxl/operators/CDXLPhysicalDML.h"
#include "naucrates/dxl/operators/CDXLPhysicalMotion.h"
#include "naucrates/dxl/operators/CDXLScalarAggref.h"
#include "naucrates/dxl/operators/CDXLScalarSubPlan.h"
#include "naucrates/md/CMDRelationGPDB.h"
#include "naucrates/md/IMDType.h"
#include "naucrates/statistics/IStatistics.h"


// fwd declarations
namespace gpopt
{
class CMDAccessor;

// dynamic array of bitsets
using CBitSetArray = CDynamicPtrArray<CBitSet, CleanupRelease>;
}  // namespace gpopt

namespace gpdxl
{
class CDXLTranslateContext;
}

namespace gpdxl
{
using namespace gpopt;

enum DistributionHashOpsKind
{
	DistrHashOpsNotDeterminedYet,
	DistrUseDefaultHashOps,
	DistrUseLegacyHashOps
};

//---------------------------------------------------------------------------
//	@class:
//		CTranslatorUtils
//
//	@doc:
//		Class providing methods for translating GPDB's PlannedStmt/Query
//		into DXL Tree
//
//---------------------------------------------------------------------------
class CTranslatorUtils
{
private:
	// Construct a set of column attnos corresponding to a single grouping set
	// from either a plain GROUP BY or one set in a list of grouping sets
	static CBitSet *CreateAttnoSetForGroupingSet(CMemoryPool *mp,
												 List *group_elems,
												 ULONG num_cols,
												 UlongToUlongMap *group_col_pos,
												 CBitSet *group_cols,
												 bool use_group_clause);

	// check if the given mdid array contains any of the polymorphic
	// types (ANYELEMENT, ANYARRAY)
	static BOOL ContainsPolymorphicTypes(IMdIdArray *mdid_array);

	// resolve polymorphic types in the given array of type ids, replacing
	// them with the actual types obtained from the query
	static IMdIdArray *ResolvePolymorphicTypes(CMemoryPool *mp,
											   IMdIdArray *return_arg_mdids,
											   List *input_arg_types,
											   FuncExpr *funcexpr);

	// update grouping col position mappings
	static void UpdateGrpColMapping(CMemoryPool *mp,
									UlongToUlongMap *grouping_col_to_pos_map,
									CBitSet *group_cols, ULONG sort_group_ref);

	// create a set of grouping sets for a rollup
	static CBitSetArray *CreateGroupingSetsForRollup(
		CMemoryPool *mp, const GroupingSet *grouping_set, ULONG num_cols,
		CBitSet *group_cols, UlongToUlongMap *group_col_pos);

	// create a set of grouping sets for a cube
	static CBitSetArray *CreateGroupingSetsForCube(
		CMemoryPool *mp, const GroupingSet *grouping_set, ULONG num_cols,
		CBitSet *group_cols, UlongToUlongMap *group_col_pos);

	// create a set of grouping sets for a grouping sets subclause
	static CBitSetArray *CreateGroupingSetsForSets(
		CMemoryPool *mp, const GroupingSet *grouping_set_node, ULONG num_cols,
		CBitSet *group_cols, UlongToUlongMap *group_col_pos);

	static CBitSetArray *CreateGroupingSetsForSimple(
		CMemoryPool *mp, const GroupingSet *grouping_set_node, ULONG num_cols,
		CBitSet *group_cols, UlongToUlongMap *group_col_pos);

public:
	struct SCmptypeStrategy
	{
		IMDType::ECmpType comptype;
		StrategyNumber strategy_no;
	};

	// get the GPDB scan direction from its corresponding DXL representation
	static ScanDirection GetScanDirection(
		EdxlIndexScanDirection idx_scan_direction);

	// find the n-th column descriptor in the table descriptor
	static const CDXLColDescr *GetColumnDescrAt(
		const CDXLTableDescr *table_descr, ULONG pos);

	// translate the join type from its GPDB representation into the DXL one
	static EdxlJoinType ConvertToDXLJoinType(JoinType jt);

	// translate the index scan direction from its GPDB representation into the DXL one
	static EdxlIndexScanDirection ConvertToDXLIndexScanDirection(
		ScanDirection sd);

	// create a DXL index descriptor from an index MD id
	static CDXLIndexDescr *GetIndexDescr(CMemoryPool *mp,
										 CMDAccessor *md_accessor, IMDId *mdid);

	// translate a RangeTableEntry into a CDXLTableDescr
	static CDXLTableDescr *GetTableDescr(CMemoryPool *mp,
										 CMDAccessor *md_accessor,
										 CIdGenerator *id_generator,
										 const RangeTblEntry *rte,
										 ULONG assigned_query_id_for_target_rel,
										 BOOL *is_distributed_table = nullptr);

	// translate a RangeTableEntry into a CDXLLogicalTVF
	static CDXLLogicalTVF *ConvertToCDXLLogicalTVF(CMemoryPool *mp,
												   CMDAccessor *md_accessor,
												   CIdGenerator *id_generator,
												   const RangeTblEntry *rte);

	// get column descriptors from a record type
	static CDXLColDescrArray *GetColumnDescriptorsFromRecord(
		CMemoryPool *mp, CIdGenerator *id_generator, List *col_names,
		List *col_types, List *col_type_modifiers);

	// get column descriptors from a record type
	static CDXLColDescrArray *GetColumnDescriptorsFromRecord(
		CMemoryPool *mp, CIdGenerator *id_generator, List *col_names,
		IMdIdArray *out_arg_types);

	// get column descriptor from a base type
	static CDXLColDescrArray *GetColumnDescriptorsFromBase(
		CMemoryPool *mp, CIdGenerator *id_generator, IMDId *mdid_return_type,
		INT type_modifier, CMDName *md_name);

	// get column descriptors from a composite type
	static CDXLColDescrArray *GetColumnDescriptorsFromComposite(
		CMemoryPool *mp, CMDAccessor *md_accessor, CIdGenerator *id_generator,
		const IMDType *md_type);

	// expand a composite type into an array of IMDColumns
	static CMDColumnArray *ExpandCompositeType(CMemoryPool *mp,
											   CMDAccessor *md_accessor,
											   const IMDType *md_type);

	// return the dxl representation of the set operation
	static EdxlSetOpType GetSetOpType(SetOperation setop, BOOL is_all);

	// construct a dynamic array of sets of column attnos corresponding
	// to the group by clause
	static CBitSetArray *GetColumnAttnosForGroupBy(
		CMemoryPool *mp, List *group_clause, List *grouping_set_list,
		ULONG num_cols, UlongToUlongMap *group_col_pos, CBitSet *group_cold);

	// return a copy of the query with constant of unknown type being coerced
	// to the common data type of the output target list
	static Query *FixUnknownTypeConstant(Query *query, List *target_list);

	// return the type of the nth non-resjunked target list entry
	static OID GetTargetListReturnTypeOid(List *target_list, ULONG col_pos);

	// construct an array of DXL column identifiers for a target list
	static ULongPtrArray *GenerateColIds(CMemoryPool *mp, List *target_list,
										 IMdIdArray *input_mdids,
										 ULongPtrArray *input_nums,
										 const BOOL *is_outer_ref,
										 CIdGenerator *colid_generator);

	// construct an array of DXL column descriptors for a target list
	// using the column ids in the given array
	static CDXLColDescrArray *GetDXLColumnDescrArray(CMemoryPool *mp,
													 List *target_list,
													 ULongPtrArray *colids,
													 BOOL keep_res_junked);

	// return the positions of the target list entries included in the output
	static ULongPtrArray *GetPosInTargetList(CMemoryPool *mp, List *target_list,
											 BOOL keep_res_junked);

	// construct a column descriptor from the given target entry, column identifier and position in the output
	static CDXLColDescr *GetColumnDescrAt(CMemoryPool *mp,
										  TargetEntry *target_entry,
										  ULONG colid, ULONG pos);

	// create a dummy project element to rename the input column identifier
	static CDXLNode *CreateDummyProjectElem(CMemoryPool *mp, ULONG colid_input,
											ULONG colid_output,
											CDXLColDescr *dxl_col_descr);

	// construct a list of colids corresponding to the given target list
	// using the given attno->colid map
	static ULongPtrArray *GetOutputColIdsArray(
		CMemoryPool *mp, List *target_list, IntToUlongMap *attno_to_colid_map);

	// construct an array of column ids for the given group by set
	static ULongPtrArray *GetGroupingColidArray(
		CMemoryPool *mp, CBitSet *group_by_cols,
		IntToUlongMap *sort_group_cols_to_colid_map);

	// return the Colid of column with given index
	static ULONG GetColId(INT index, IntToUlongMap *index_to_colid_map);

	// return the corresponding ColId for the given varno, varattno and querylevel
	static ULONG GetColId(ULONG query_level, INT varno, INT var_attno,
						  IMDId *mdid, CMappingVarColId *var_colid_mapping);

	// check to see if the target list entry is a sorting column
	static BOOL IsSortingColumn(const TargetEntry *target_entry,
								List *sort_clause_list);
	// check to see if the target list entry is used in the window reference
	static BOOL IsReferencedInWindowSpec(const TargetEntry *target_entry,
										 List *window_clause_list);

	// check if the project list contains AggRef with ORDER BY
	static BOOL HasOrderedAggRefInProjList(CDXLNode *proj_list_dxlnode);

	// extract a matching target entry that is a window spec
	static TargetEntry *GetWindowSpecTargetEntry(Node *node,
												 List *window_clause_list,
												 List *target_list);

	// create a scalar const value expression for the given int8 value
	static CDXLNode *CreateDXLProjElemFromInt8Const(CMemoryPool *mp,
													CMDAccessor *md_accessor,
													INT val);

	// check to see if the target list entry is a grouping column
	static BOOL IsGroupingColumn(const TargetEntry *target_entry,
								 List *group_clause_list);

	// check to see if the target list entry is a grouping column
	static BOOL IsGroupingColumn(const TargetEntry *target_entry,
								 const SortGroupClause *sort_group_clause);

	// check if the expression has a matching target entry that is a grouping column
	static BOOL IsGroupingColumn(Node *node, List *group_clause_list,
								 List *target_list);

	// extract a matching target entry that is a grouping column
	static TargetEntry *GetGroupingColumnTargetEntry(Node *node,
													 List *group_clause_list,
													 List *target_list);

	// convert a list of column ids to a list of attribute numbers using
	// the provided context with mappings
	static List *ConvertColidToAttnos(ULongPtrArray *pdrgpul,
									  CDXLTranslateContext *dxl_translate_ctxt);

	// parse string value into a Long Integer
	static LINT GetLongFromStr(const CWStringBase *wcstr);

	// parse string value into an Integer
	static INT GetIntFromStr(const CWStringBase *wcstr);

	// check whether the given project list has a project element of the given
	// operator type
	static BOOL HasProjElem(CDXLNode *project_list_dxlnode, Edxlopid dxl_op_id);

	// create a multi-byte character string from a wide character string
	static CHAR *CreateMultiByteCharStringFromWCString(const WCHAR *wcstr);

	static UlongToUlongMap *MakeNewToOldColMapping(CMemoryPool *mp,
												   ULongPtrArray *old_colids,
												   ULongPtrArray *new_colids);

	// check if the given tree contains a subquery
	static BOOL HasSubquery(Node *node);

	// check if the given function is a SIRV (single row volatile) that reads
	// or modifies SQL data
	static BOOL IsSirvFunc(CMemoryPool *mp, CMDAccessor *md_accessor,
						   OID func_oid);

	// is this a motion sensitive to duplicates
	static BOOL IsDuplicateSensitiveMotion(
		gpdxl::CDXLPhysicalMotion *dxl_motion);

	// construct a project element with a const NULL expression
	static CDXLNode *CreateDXLProjElemConstNULL(CMemoryPool *mp,
												CMDAccessor *md_accessor,
												IMDId *mdid, ULONG colid,
												const WCHAR *col_name);

	// construct a project element with a const NULL expression
	static CDXLNode *CreateDXLProjElemConstNULL(CMemoryPool *mp,
												CMDAccessor *md_accessor,
												IMDId *mdid, ULONG colid,
												CHAR *alias_name);

	// create a DXL project element node with a Const NULL of type provided
	// by the column descriptor
	static CDXLNode *CreateDXLProjElemConstNULL(CMemoryPool *mp,
												CMDAccessor *md_accessor,
												CIdGenerator *colid_generator,
												const IMDColumn *col);

	// check required permissions for the range table
	static void CheckRTEPermissions(List *range_table_list);

	// check if given column ids are outer references in the tree rooted by given node
	static void MarkOuterRefs(ULONG *colid, BOOL *is_outer_ref,
							  ULONG num_columns, CDXLNode *node);

	// map DXL Subplan type to GPDB SubLinkType
	static SubLinkType MapDXLSubplanToSublinkType(
		gpdxl::EdxlSubPlanType dxl_subplan_type);

	// map GPDB SubLinkType to DXL Subplan type
	static EdxlSubPlanType MapSublinkTypeToDXLSubplan(SubLinkType slink);

	// check whether there are triggers for the given operation on
	// the given relation
	static BOOL RelHasTriggers(CMemoryPool *mp, CMDAccessor *md_accessor,
							   const IMDRelation *mdrel,
							   const gpdxl::EdxlDmlType dml_type_dxl);

	// check whether the given trigger is applicable to the given DML operation
	static BOOL IsApplicableTrigger(CMDAccessor *md_accessor,
									IMDId *trigger_mdid,
									const gpdxl::EdxlDmlType dml_type_dxl);

	// check whether there are NOT NULL or CHECK constraints for the given relation
	static BOOL RelHasConstraints(const IMDRelation *rel);

	// translate the list of error messages from an assert constraint list
	static List *GetAssertErrorMsgs(CDXLNode *assert_constraint_list);

	// return the count of non-system columns in the relation
	static ULONG GetNumNonSystemColumns(const IMDRelation *mdrel);

	// return agg kind as an EdxlAggrefKind
	static EdxlAggrefKind GetAggKind(CHAR aggkind);

	// return agg kind as a CHAR
	static CHAR GetAggKind(EdxlAggrefKind aggkind);

	// check if const func returns composite type
	static BOOL IsCompositeConst(CMemoryPool *mp, CMDAccessor *md_accessor,
								 const RangeTblFunction *rtfunc);

	// check if rel contains foreign partitions
	static BOOL RelContainsForeignPartitions(const IMDRelation *rel,
											 CMDAccessor *md_accessor);
};
}  // namespace gpdxl

#endif	// !GPDXL_CTranslatorUtils_H

// EOF
