//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2012 EMC Corp.
//
//	@filename:
//		gpdbwrappers.h
//
//	@doc:
//		Definition of GPDB function wrappers
//
//	@test:
//
//
//---------------------------------------------------------------------------
#ifndef GPDB_gpdbwrappers_H
#define GPDB_gpdbwrappers_H

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

#include "access/amapi.h"
#include "access/attnum.h"
#include "optimizer/plancat.h"
#include "parser/parse_coerce.h"
#include "statistics/statistics.h"
#include "utils/faultinjector.h"
#include "utils/lsyscache.h"
}

#include "gpos/types.h"

// fwd declarations
using SysScanDesc = struct SysScanDescData *;
struct TypeCacheEntry;
using Numeric = struct NumericData *;
using HeapTuple = struct HeapTupleData *;
using Relation = struct RelationData *;
struct Value;
using TupleDesc = struct TupleDescData *;
struct Query;
using ScanKey = struct ScanKeyData *;
struct Bitmapset;
struct Plan;
struct ListCell;
struct TargetEntry;
struct Expr;
struct ExtTableEntry;
struct ForeignScan;
struct Uri;
struct CdbComponentDatabases;
struct StringInfoData;
using StringInfo = StringInfoData *;
struct LogicalIndexes;
struct ParseState;
struct DefElem;
struct GpPolicy;
struct PartitionSelector;
struct Motion;
struct Var;
struct Const;
struct ArrayExpr;

#include "gpopt/utils/RelationWrapper.h"

namespace gpdb
{
// convert datum to bool
bool BoolFromDatum(Datum d);

// convert bool to datum
Datum DatumFromBool(bool b);

// convert datum to char
char CharFromDatum(Datum d);

// convert char to datum
Datum DatumFromChar(char c);

// convert datum to int8
int8 Int8FromDatum(Datum d);

// convert int8 to datum
Datum DatumFromInt8(int8 i8);

// convert datum to uint8
uint8 Uint8FromDatum(Datum d);

// convert uint8 to datum
Datum DatumFromUint8(uint8 ui8);

// convert datum to int16
int16 Int16FromDatum(Datum d);

// convert int16 to datum
Datum DatumFromInt16(int16 i16);

// convert datum to uint16
uint16 Uint16FromDatum(Datum d);

// convert uint16 to datum
Datum DatumFromUint16(uint16 ui16);

// convert datum to int32
int32 Int32FromDatum(Datum d);

// convert int32 to datum
Datum DatumFromInt32(int32 i32);

// convert datum to uint32
uint32 lUint32FromDatum(Datum d);

// convert uint32 to datum
Datum DatumFromUint32(uint32 ui32);

// convert datum to int64
int64 Int64FromDatum(Datum d);

// convert int64 to datum
Datum DatumFromInt64(int64 i64);

// convert datum to uint64
uint64 Uint64FromDatum(Datum d);

// convert uint64 to datum
Datum DatumFromUint64(uint64 ui64);

// convert datum to oid
Oid OidFromDatum(Datum d);

// convert datum to generic object with pointer handle
void *PointerFromDatum(Datum d);

// convert datum to float4
float4 Float4FromDatum(Datum d);

// convert datum to float8
float8 Float8FromDatum(Datum d);

// convert pointer to datum
Datum DatumFromPointer(const void *p);

// does an aggregate exist with the given oid
bool AggregateExists(Oid oid);

// add member to Bitmapset
Bitmapset *BmsAddMember(Bitmapset *a, int x);

// create a copy of an object
void *CopyObject(void *from);

// datum size
Size DatumSize(Datum value, bool type_by_val, int type_len);

bool ExpressionReturnsSet(Node *clause);

// expression type
Oid ExprType(Node *expr);

// expression type modifier
int32 ExprTypeMod(Node *expr);

// expression collation
Oid ExprCollation(Node *expr);

// expression collation - GPDB_91_MERGE_FIXME
Oid TypeCollation(Oid type);

// extract nodes with specific tag from a plan tree
List *ExtractNodesPlan(Plan *pl, int node_tag, bool descend_into_subqueries);

// extract nodes with specific tag from an expression tree
List *ExtractNodesExpression(Node *node, int node_tag,
							 bool descend_into_subqueries);

// intermediate result type of given aggregate
Oid GetAggIntermediateResultType(Oid aggid);

// Identify the specific datatypes passed to an aggregate call.
int GetAggregateArgTypes(Aggref *aggref, Oid *inputTypes);

// Identify the transition state value's datatype for an aggregate call.
Oid ResolveAggregateTransType(Oid aggfnoid, Oid aggtranstype, Oid *inputTypes,
							  int numArguments);

// replace Vars that reference JOIN outputs with references to the original
// relation variables instead
Query *FlattenJoinAliasVar(Query *query, gpos::ULONG query_level);

// is aggregate ordered
bool IsOrderedAgg(Oid aggid);

bool IsRepSafeAgg(Oid aggid);

// does aggregate have a combine function (and serial/deserial functions, if needed)
bool IsAggPartialCapable(Oid aggid);

// intermediate result type of given aggregate
Oid GetAggregate(const char *agg, Oid type_oid);

// array type oid
Oid GetArrayType(Oid typid);

// attribute stats slot
bool GetAttrStatsSlot(AttStatsSlot *sslot, HeapTuple statstuple, int reqkind,
					  Oid reqop, int flags);

// free attribute stats slot
void FreeAttrStatsSlot(AttStatsSlot *sslot);

// attribute statistics
HeapTuple GetAttStats(Oid relid, AttrNumber attnum);

List *GetExtStats(Relation rel);

char *GetExtStatsName(Oid statOid);
List *GetExtStatsKinds(Oid statOid);

// does a function exist with the given oid
bool FunctionExists(Oid oid);

// is the given function an allowed lossy cast for PS
bool IsFuncAllowedForPartitionSelection(Oid funcid);

// is the given function strict
bool FuncStrict(Oid funcid);

// does this preserve the NDVs of its inputs?
bool IsFuncNDVPreserving(Oid funcid);

// stability property of given function
char FuncStability(Oid funcid);

// exec location property of given function
char FuncExecLocation(Oid funcid);

// check constraint name
char *GetCheckConstraintName(Oid check_constraint_oid);

// check constraint relid
Oid GetCheckConstraintRelid(Oid check_constraint_oid);

// check constraint expression tree
Node *PnodeCheckConstraint(Oid check_constraint_oid);

// get the list of check constraints for a given relation
List *GetCheckConstraintOids(Oid rel_oid);

// part constraint expression tree
Node *GetRelationPartConstraints(Relation rel);

// get the cast function for the specified source and destination types
bool GetCastFunc(Oid src_oid, Oid dest_oid, bool *is_binary_coercible,
				 Oid *cast_fn_oid, CoercionPathType *pathtype);

// get type of operator
unsigned int GetComparisonType(Oid op_oid);

// get scalar comparison between given types
Oid GetComparisonOperator(Oid left_oid, Oid right_oid, unsigned int cmpt);

// get equality operator for given type
Oid GetEqualityOp(Oid type_oid);

// get equality operator for given ordering op (i.e. < or >)
Oid GetEqualityOpForOrderingOp(Oid opno, bool *reverse);

// get ordering operator for given equality op (i.e. =)
Oid GetOrderingOpForEqualityOp(Oid opno, bool *reverse);

// function name
char *GetFuncName(Oid funcid);

// output argument types of the given function
List *GetFuncOutputArgTypes(Oid funcid);

// argument types of the given function
List *GetFuncArgTypes(Oid funcid);

// does a function return a set of rows
bool GetFuncRetset(Oid funcid);

// return type of the given function
Oid GetFuncRetType(Oid funcid);

// commutator operator of the given operator
Oid GetCommutatorOp(Oid opno);

// inverse operator of the given operator
Oid GetInverseOp(Oid opno);

// function oid corresponding to the given operator oid
RegProcedure GetOpFunc(Oid opno);

// operator name
char *GetOpName(Oid opno);

// keys of the relation with the given oid
List *GetRelationKeys(Oid relid);

// relid of a composite type
Oid GetTypeRelid(Oid typid);

// name of the type with the given oid
char *GetTypeName(Oid typid);

// number of GP segments
int GetGPSegmentCount(void);

// heap attribute is null
bool HeapAttIsNull(HeapTuple tup, int attnum);

// free heap tuple
void FreeHeapTuple(HeapTuple htup);

// get the default hash opclass for type
Oid GetDefaultDistributionOpclassForType(Oid typid);

// get the column-definition hash opclass for type
Oid GetColumnDefOpclassForType(List *opclassName, Oid typid);

// get the default hash opfamily for type
Oid GetDefaultDistributionOpfamilyForType(Oid typid);
Oid GetDefaultPartitionOpfamilyForType(Oid typid);

// get the hash function in an opfamily for given datatype
Oid GetHashProcInOpfamily(Oid opfamily, Oid typid);

// is the given hash function a legacy cdbhash function?
Oid IsLegacyCdbHashFunction(Oid hashfunc);

// is the given hash function a legacy cdbhash function?
Oid GetLegacyCdbHashOpclassForBaseType(Oid typid);

// return the operator family the given opclass belongs to
Oid GetOpclassFamily(Oid opclass);

// append an element to a list
List *LAppend(List *list, void *datum);

// append an integer to a list
List *LAppendInt(List *list, int datum);

// append an oid to a list
List *LAppendOid(List *list, Oid datum);

// prepend a new element to the list
List *LPrepend(void *datum, List *list);

// prepend an integer to the list
List *LPrependInt(int datum, List *list);

// prepend an oid to a list
List *LPrependOid(Oid datum, List *list);

// concatenate lists
List *ListConcat(List *list1, List *list2);

// copy list
List *ListCopy(List *list);

// first cell in a list
ListCell *ListHead(List *l);

// last cell in a list
ListCell *ListTail(List *l);

// number of items in a list
uint32 ListLength(List *l);

// return the nth element in a list of pointers
void *ListNth(List *list, int n);

// return the nth element in a list of ints
int ListNthInt(List *list, int n);

// return the nth element in a list of oids
Oid ListNthOid(List *list, int n);

// check whether the given oid is a member of the given list
bool ListMemberOid(List *list, Oid oid);

// free list
void ListFree(List *list);

// deep free of a list
void ListFreeDeep(List *list);

// lookup type cache
TypeCacheEntry *LookupTypeCache(Oid type_id, int flags);

// create a value node for a string
Value *MakeStringValue(char *str);

// create a value node for an integer
Value *MakeIntegerValue(long i);

// create a constant of type int4
Node *MakeIntConst(int32 intValue);

// create a bool constant
Node *MakeBoolConst(bool value, bool isnull);

// make a NULL constant of the given type
Node *MakeNULLConst(Oid type_oid);

// make a NULL constant of the given type
Node *MakeSegmentFilterExpr(int segid);

// create a new target entry
TargetEntry *MakeTargetEntry(Expr *expr, AttrNumber resno, char *resname,
							 bool resjunk);

// create a new var node
Var *MakeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod,
			 Index varlevelsup);

// memory allocation functions
void *MemCtxtAllocZeroAligned(MemoryContext context, Size size);
void *MemCtxtAllocZero(MemoryContext context, Size size);
void *MemCtxtRealloc(void *pointer, Size size);
void *GPDBAlloc(Size size);
void GPDBFree(void *ptr);

// create a duplicate of the given string in the given memory context
char *MemCtxtStrdup(MemoryContext context, const char *string);

// similar to ereport for logging messages
void GpdbEreportImpl(int xerrcode, int severitylevel, const char *xerrmsg,
					 const char *xerrhint, const char *filename, int lineno,
					 const char *funcname);
#define GpdbEreport(xerrcode, severitylevel, xerrmsg, xerrhint)       \
	gpdb::GpdbEreportImpl(xerrcode, severitylevel, xerrmsg, xerrhint, \
						  __FILE__, __LINE__, PG_FUNCNAME_MACRO)

// string representation of a node
char *NodeToString(void *obj);

// return the default value of the type
Node *GetTypeDefault(Oid typid);

// convert numeric to double; if out of range, return +/- HUGE_VAL
double NumericToDoubleNoOverflow(Numeric num);

// is the given Numeric value NaN?
bool NumericIsNan(Numeric num);

// convert time-related datums to double for stats purpose
double ConvertTimeValueToScalar(Datum datum, Oid typid);

// convert network-related datums to double for stats purpose
double ConvertNetworkToScalar(Datum datum, Oid typid);

// is the given operator hash-joinable
bool IsOpHashJoinable(Oid opno, Oid inputtype);

// is the given operator merge-joinable
bool IsOpMergeJoinable(Oid opno, Oid inputtype);

// is the given operator strict
bool IsOpStrict(Oid opno);

// does it preserve the NDVs of its inputs
bool IsOpNDVPreserving(Oid opno);

// get input types for a given operator
void GetOpInputTypes(Oid opno, Oid *lefttype, Oid *righttype);

// expression tree walker
bool WalkExpressionTree(Node *node, bool (*walker)(), void *context);

// query or expression tree walker
bool WalkQueryOrExpressionTree(Node *node, bool (*walker)(), void *context,
							   int flags);

// modify the components of a Query tree
Query *MutateQueryTree(Query *query, Node *(*mutator)(), void *context,
					   int flags);

// modify an expression tree
Node *MutateExpressionTree(Node *node, Node *(*mutator)(), void *context);

// modify a query or an expression tree
Node *MutateQueryOrExpressionTree(Node *node, Node *(*mutator)(), void *context,
								  int flags);

// check whether a relation is inherited
bool HasSubclassSlow(Oid rel_oid);

// return the distribution policy of a relation; if the table is partitioned
// and the parts are distributed differently, return Random distribution
GpPolicy *GetDistributionPolicy(Relation rel);

// return true if the table is partitioned and hash-distributed, and one of
// the child partitions is randomly distributed
gpos::BOOL IsChildPartDistributionMismatched(Relation rel);

double CdbEstimatePartitionedNumTuples(Relation rel);

PageEstimate CdbEstimatePartitionedNumPages(Relation rel);

// close the given relation
void CloseRelation(Relation rel);

// return a list of index oids for a given relation
List *GetRelationIndexes(Relation relation);

// build an array of triggers for this relation
void BuildRelationTriggers(Relation rel);

MVNDistinct *GetMVNDistinct(Oid stat_oid);

MVDependencies *GetMVDependencies(Oid stat_oid);

// get relation with given oid
RelationWrapper GetRelation(Oid rel_oid);

// get ForeignScan node to scan a foreign table
ForeignScan *CreateForeignScan(Oid rel_oid, Index scanrelid, List *qual,
							   List *targetlist, Query *query,
							   RangeTblEntry *rte);

// return the first member of the given targetlist whose expression is
// equal to the given expression, or NULL if no such member exists
TargetEntry *FindFirstMatchingMemberInTargetList(Node *node, List *targetlist);

// return a list of members of the given targetlist whose expression is
// equal to the given expression, or NULL if no such member exists
List *FindMatchingMembersInTargetList(Node *node, List *targetlist);

// check if two gpdb objects are equal
bool Equals(void *p1, void *p2);

// check whether a type is composite
bool IsCompositeType(Oid typid);

bool IsTextRelatedType(Oid typid);

// create an empty 'StringInfoData' & return a pointer to it
StringInfo MakeStringInfo(void);

// append the two given strings to the StringInfo object
void AppendStringInfo(StringInfo str, const char *str1, const char *str2);

// look for the given node tags in the given tree and return the index of
// the first one found, or -1 if there are none
int FindNodes(Node *node, List *nodeTags);

// GPDB_91_MERGE_FIXME: collation
// look for nodes with non-default collation; returns 1 if any exist, -1 otherwise
int CheckCollation(Node *node);

Node *CoerceToCommonType(ParseState *pstate, Node *node, Oid target_type,
						 const char *context);

// replace any polymorphic type with correct data type deduced from input arguments
bool ResolvePolymorphicArgType(int numargs, Oid *argtypes, char *argmodes,
							   FuncExpr *call_expr);

// hash a list of const values with GPDB's hash function
int32 CdbHashConstList(List *constants, int num_segments, Oid *hashfuncs);

// get a random segment number
unsigned int CdbHashRandomSeg(int num_segments);

// check permissions on range table
void CheckRTPermissions(List *rtable);

// throw an error if table has update triggers.
bool HasUpdateTriggers(Oid relid);

// get index operator family properties
void IndexOpProperties(Oid opno, Oid opfamily, StrategyNumber *strategynumber,
					   Oid *righttype);

// check whether index column is returnable (for index-only scans)
gpos::BOOL IndexCanReturn(Relation index, int attno);

// get oids of families this operator belongs to
List *GetOpFamiliesForScOp(Oid opno);

// get the OID of hash equality operator(s) compatible with the given op
Oid GetCompatibleHashOpFamily(Oid opno);

// get the OID of legacy hash equality operator(s) compatible with the given op
Oid GetCompatibleLegacyHashOpFamily(Oid opno);

// get oids of op classes for the index keys
List *GetIndexOpFamilies(Oid index_oid);

// get oids of op classes for the merge join
List *GetMergeJoinOpFamilies(Oid opno);

// get the OID of base elementtype fora given typid
Oid GetBaseType(Oid typid);

// returns the result of evaluating 'expr' as an Expr. Caller keeps ownership of 'expr'
// and takes ownership of the result
Expr *EvaluateExpr(Expr *expr, Oid result_type, int32 typmod);

// extract string value from defelem's value
char *DefGetString(DefElem *defelem);

// transform array Const to an ArrayExpr
Expr *TransformArrayConstToArrayExpr(Const *constant);

// transform array Const to an ArrayExpr
Node *EvalConstExpressions(Node *node);

#ifdef FAULT_INJECTOR
// simple fault injector used by COptTasks.cpp to inject GPDB fault
FaultInjectorType_e InjectFaultInOptTasks(const char *fault_name);
#endif

// Does the metadata cache need to be reset (because of a catalog
// table has been changed?)
bool MDCacheNeedsReset(void);

// returns true if a query cancel is requested in GPDB
bool IsAbortRequested(void);

// Given the type OID, get the typelem (InvalidOid if not an array type).
Oid GetElementType(Oid array_type_oid);

GpPolicy *MakeGpPolicy(GpPolicyType ptype, int nattrs, int numsegments);


uint32 HashChar(Datum d);

uint32 HashBpChar(Datum d);

uint32 HashText(Datum d);

uint32 HashName(Datum d);

uint32 UUIDHash(Datum d);

void *GPDBMemoryContextAlloc(MemoryContext context, Size size);

MemoryContext GPDBAllocSetContextCreate();

void GPDBMemoryContextDelete(MemoryContext context);

List *GetRelChildIndexes(Oid reloid);

Oid GetForeignServerId(Oid reloid);

void GPDBLockRelationOid(Oid reloid, int lockmode);

char *GetRelFdwName(Oid reloid);

PathTarget *MakePathtargetFromTlist(List *tlist);

void SplitPathtargetAtSrfs(PlannerInfo *root, PathTarget *target,
						   PathTarget *input_target, List **targets,
						   List **targets_contain_srfs);

List *MakeTlistFromPathtarget(PathTarget *target);

Node *Expression_tree_mutator(Node *node, Node *(*mutator)(), void *context);

TargetEntry *TlistMember(Expr *node, List *targetlist);

Var *MakeVarFromTargetEntry(Index varno, TargetEntry *tle);

TargetEntry *FlatCopyTargetEntry(TargetEntry *src_tle);

bool IsTypeRange(Oid typid);

char *GetRelAmName(Oid reloid);

IndexAmRoutine *GetIndexAmRoutineFromAmHandler(Oid am_handler);

PartitionDesc GPDBRelationRetrievePartitionDesc(Relation rel);

PartitionKey GPDBRelationRetrievePartitionKey(Relation rel);

bool TestexprIsHashable(Node *testexpr, List *param_ids);

gpos::BOOL WalkQueryTree(Query *query, bool (*walker)(), void *context,
						 int flags);

}  //namespace gpdb

#define ForEach(cell, l) \
	for ((cell) = gpdb::ListHead(l); (cell) != NULL; (cell) = lnext(cell))

#define ForBoth(cell1, list1, cell2, list2)                                \
	for ((cell1) = gpdb::ListHead(list1), (cell2) = gpdb::ListHead(list2); \
		 (cell1) != NULL && (cell2) != NULL;                               \
		 (cell1) = lnext(cell1), (cell2) = lnext(cell2))

#define ForThree(cell1, list1, cell2, list2, cell3, list3)                 \
	for ((cell1) = gpdb::ListHead(list1), (cell2) = gpdb::ListHead(list2), \
		(cell3) = gpdb::ListHead(list3);                                   \
		 (cell1) != NULL && (cell2) != NULL && (cell3) != NULL;            \
		 (cell1) = lnext(cell1), (cell2) = lnext(cell2),                   \
		(cell3) = lnext(cell3))

#define ForEachWithCount(cell, list, counter)                          \
	for ((cell) = gpdb::ListHead(list), (counter) = 0; (cell) != NULL; \
		 (cell) = lnext(cell), ++(counter))

#define ListMake1(x1) gpdb::LPrepend(x1, NIL)

#define ListMake2(x1, x2) gpdb::LPrepend(x1, ListMake1(x2))

#define ListMake1Int(x1) gpdb::LPrependInt(x1, NIL)

#define ListMake1Oid(x1) gpdb::LPrependOid(x1, NIL)
#define ListMake2Oid(x1, x2) gpdb::LPrependOid(x1, ListMake1Oid(x2))

#define LInitial(l) lfirst(gpdb::ListHead(l))

#define LInitialOID(l) lfirst_oid(gpdb::ListHead(l))

#define Palloc0Fast(sz)                                              \
	(MemSetTest(0, (sz))                                             \
		 ? gpdb::MemCtxtAllocZeroAligned(CurrentMemoryContext, (sz)) \
		 : gpdb::MemCtxtAllocZero(CurrentMemoryContext, (sz)))

#ifdef __GNUC__

/* With GCC, we can use a compound statement within an expression */
#define NewNode(size, tag)                                                \
	({                                                                    \
		Node *_result;                                                    \
		AssertMacro((size) >= sizeof(Node)); /* need the tag, at least */ \
		_result = (Node *) Palloc0Fast(size);                             \
		_result->type = (tag);                                            \
		_result;                                                          \
	})
#else

/*
 *	There is no way to dereference the palloc'ed pointer to assign the
 *	tag, and also return the pointer itself, so we need a holder variable.
 *	Fortunately, this macro isn't recursive so we just define
 *	a global variable for this purpose.
 */
extern PGDLLIMPORT Node *newNodeMacroHolder;

#define NewNode(size, tag)                                             \
	(AssertMacro((size) >= sizeof(Node)), /* need the tag, at least */ \
	 newNodeMacroHolder = (Node *) Palloc0Fast(size),                  \
	 newNodeMacroHolder->type = (tag), newNodeMacroHolder)
#endif	// __GNUC__

#define MakeNode(_type_) ((_type_ *) NewNode(sizeof(_type_), T_##_type_))

#define PStrDup(str) gpdb::MemCtxtStrdup(CurrentMemoryContext, (str))

#endif	// !GPDB_gpdbwrappers_H

// EOF
