//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2011 EMC Corp.
//
//	@filename:
//		CTestUtils.h
//
//	@doc:
//		Optimizer test utility functions
//---------------------------------------------------------------------------
#ifndef GPOPT_CTestUtils_H
#define GPOPT_CTestUtils_H

#include "gpos/base.h"

#include "gpopt/base/CAutoOptCtxt.h"
#include "gpopt/base/CColRef.h"
#include "gpopt/base/CQueryContext.h"
#include "gpopt/base/CRange.h"
#include "gpopt/engine/CEnumeratorConfig.h"
#include "gpopt/mdcache/CMDAccessor.h"
#include "gpopt/metadata/CTableDescriptor.h"
#include "gpopt/operators/CExpression.h"
#include "gpopt/operators/CLogicalGet.h"
#include "gpopt/operators/CScalarArrayCmp.h"
#include "gpopt/operators/CScalarBoolOp.h"
#include "gpopt/operators/CScalarConst.h"
#include "gpopt/operators/CScalarProjectList.h"
#include "gpopt/operators/CScalarSubquery.h"
#include "naucrates/md/CMDTypeInt2GPDB.h"
#include "naucrates/md/CMDTypeInt4GPDB.h"
#include "naucrates/md/CMDTypeInt8GPDB.h"
#include "naucrates/md/CSystemId.h"
#include "naucrates/statistics/CPoint.h"

#include "unittest/base.h"


#define GPOPT_MDCACHE_TEST_OID 1234
#define GPOPT_MDCACHE_TEST_OID_PARTITIONED 5678
#define GPOPT_MDCACHE_TEST_OID_PARTITIONED_WITH_INDEXES 5369655
#define GPOPT_TEST_REL_WIDTH 3	// number of columns in generated test relations
#define GPOPT_TEST_PART_INDEX 1234567

#define GPOPT_TEST_REL_OID1 OID(11111)
#define GPOPT_TEST_REL_OID2 OID(22222)
#define GPOPT_TEST_REL_OID3 OID(33333)
#define GPOPT_TEST_REL_OID4 OID(44444)
#define GPOPT_TEST_REL_OID5 OID(55555)
#define GPOPT_TEST_REL_OID6 OID(66666)
#define GPOPT_TEST_REL_OID7 OID(77777)
#define GPOPT_TEST_REL_OID8 OID(88888)
#define GPOPT_TEST_REL_OID9 OID(99999)
#define GPOPT_TEST_REL_OID10 OID(10000)
#define GPOPT_TEST_REL_OID11 OID(10111)
#define GPOPT_TEST_REL_OID12 OID(10222)
#define GPOPT_TEST_REL_OID13 OID(10333)
#define GPOPT_TEST_REL_OID14 OID(10444)
#define GPOPT_TEST_REL_OID15 OID(10555)
#define GPOPT_TEST_REL_OID16 OID(10666)
#define GPOPT_TEST_REL_OID17 OID(10777)
#define GPOPT_TEST_REL_OID18 OID(10888)
#define GPOPT_TEST_REL_OID19 OID(10999)
#define GPOPT_TEST_REL_OID20 OID(10200)
#define GPOPT_TEST_REL_OID21 OID(10311)
#define GPOPT_TEST_REL_OID22 OID(27118)

#define GPDB_INT4_LT_OP OID(97)
#define GPDB_INT4_EQ_OP OID(96)
#define GPDB_INT4_NEQ_OP OID(518)
#define GPDB_INT4_SUM_AGG OID(2114)
#define GPDB_INT8_GENERATE_SERIES OID(1067)

// forward declarations
namespace gpmd
{
class CMDIdGPDB;
}

namespace gpopt
{
using namespace gpos;
using gpmd::CMDIdGPDB;

// forward declarations
class CConstraintInterval;
class IConstExprEvaluator;

using CExpressionJoinsArray = CDynamicPtrArray<CExpression, CleanupNULL>;

using FnDXLPlanChecker = BOOL(CDXLNode *);

//---------------------------------------------------------------------------
//	@class:
//		CTestUtils
//
//	@doc:
//		Test utility functions
//
//---------------------------------------------------------------------------
class CTestUtils
{
private:
	// local memory pool
	static CMemoryPool *m_mp;

	// construct an array of segment ids
	static gpdxl::IntPtrArray *PdrgpiSegments(CMemoryPool *mp);

	// generate minidump file name from passed file name
	static CHAR *SzMinidumpFileName(CMemoryPool *mp, const CHAR *file_name);

public:
	// pair of DXL query file and the corresponding DXL plan file
	struct STestCase
	{
		const CHAR *szInputFile;
		const CHAR *szOutputFile;
	};

	//---------------------------------------------------------------------------
	//	@class:
	//		CTestUtils
	//
	//	@doc:
	//		Set up basic structures needed for most tests
	//
	//---------------------------------------------------------------------------
	class CTestSetup
	{
	private:
		// memory pool
		CAutoMemoryPool m_amp;

		// metadata accessor
		CMDAccessor m_mda;

		// optimization context
		CAutoOptCtxt m_aoc;

		// set up a file based provider
		static CMDProviderMemory *PmdpSetupFileBasedProvider();

	public:
		// private ctor
		CTestSetup(const CTestSetup &) = delete;

		// ctor
		CTestSetup();

		// return the memory pool
		CMemoryPool *
		Pmp()
		{
			return m_amp.Pmp();
		}

		// return the metadata acccessor
		CMDAccessor *
		Pmda()
		{
			return &m_mda;
		}
	};


	//-------------------------------------------------------------------
	// Initialization/Destruction of provider file
	//-------------------------------------------------------------------
	// initialize provider file
	static void InitProviderFile(CMemoryPool *mp);

	// destroy metadata provider
	static void DestroyMDProvider();

	//-------------------------------------------------------------------
	// Helpers for generating expressions
	//-------------------------------------------------------------------

	// generate a plain table descriptor
	static CTableDescriptor *PtabdescPlain(CMemoryPool *mp, ULONG num_cols,
										   IMDId *mdid, const CName &nameTable,
										   BOOL is_nullable = false);

	// generate a plain table descriptor, where the column names are generated using a format string containing %d
	static CTableDescriptor *PtabdescPlainWithColNameFormat(
		CMemoryPool *mp, ULONG num_cols, IMDId *mdid,
		const WCHAR *wszColNameFormat, const CName &nameTable,
		BOOL is_nullable = false);

	// generate a table descriptor
	static CTableDescriptor *PtabdescCreate(CMemoryPool *mp, ULONG num_cols,
											IMDId *mdid, const CName &name,
											BOOL fPartitioned = false);

	// generate a get expression
	static CExpression *PexprLogicalGet(CMemoryPool *mp,
										CTableDescriptor *ptabdesc,
										const CWStringConst *pstrTableAlias);

	// generate a get expression over table with nullable columns
	static CExpression *PexprLogicalGetNullable(
		CMemoryPool *mp, OID oidTable, const CWStringConst *str_table_name,
		const CWStringConst *pstrTableAlias);

	// generate a get expression
	static CExpression *PexprLogicalGet(CMemoryPool *mp,
										CWStringConst *str_table_name,
										CWStringConst *pstrTableAlias,
										ULONG ulTableId);

	// generate a random get expression
	static CExpression *PexprLogicalGet(CMemoryPool *mp);

	// generate a random foreign get expression
	static CExpression *PexprLogicalForeignGet(CMemoryPool *mp);

	// generate a random assert expression
	static CExpression *PexprLogicalAssert(CMemoryPool *mp);

	// generate a random get expression on a partitioned table
	static CExpression *PexprLogicalGetPartitioned(CMemoryPool *mp);

	// generate a randomized get expression for a partitioned table with indexes
	static CExpression *PexprLogicalDynamicGetWithIndexes(CMemoryPool *mp);

	// generate a select expression with random equality predicate
	static CExpression *PexprLogicalSelect(CMemoryPool *mp, CExpression *pexpr);

	// generate a select expression
	static CExpression *PexprLogicalSelect(CMemoryPool *mp,
										   CWStringConst *str_table_name,
										   CWStringConst *pstrTableAlias,
										   ULONG ulTableId);

	// generate a random select expression
	static CExpression *PexprLogicalSelect(CMemoryPool *mp);

	// generate a select expression with a contradiction
	static CExpression *PexprLogicalSelectWithContradiction(CMemoryPool *mp);

	// generate a select on top of a partitioned table get with a filter on the part key
	static CExpression *PexprLogicalSelectPartitioned(CMemoryPool *mp);

	// generate a project element with sum agg function
	static CExpression *PexprPrjElemWithSum(CMemoryPool *mp, CColRef *colref);

	// generate an join of specific join type on  get with a predicate
	// on the part key with a partitioned table on the outer side
	template <class T>
	static CExpression *PexprJoinPartitionedOuter(CMemoryPool *mp);

	// generate a join of specific join type on  get with a predicate
	// on the part key with a partitioned table on the inner side
	template <class T>
	static CExpression *PexprJoinPartitionedInner(CMemoryPool *mp);

	// generate an join of specific join type on top of a partitioned
	// table get with a predicate on the part key
	template <class T>
	static CExpression *PexprJoinPartitioned(CMemoryPool *mp,
											 BOOL fOuterPartitioned);

	// generate an 3-way join including a partitioned table
	static CExpression *Pexpr3WayJoinPartitioned(CMemoryPool *mp);

	// generate an 4-way join including a partitioned table
	static CExpression *Pexpr4WayJoinPartitioned(CMemoryPool *mp);

	// generate a random select expression with nested AND tree
	static CExpression *PexprLogicalSelectWithNestedAnd(CMemoryPool *mp);

	// generate a random select expression with nested OR tree
	static CExpression *PexprLogicalSelectWithNestedOr(CMemoryPool *mp);

	// generate a random select expression with an even number of nested NOT nodes
	static CExpression *PexprLogicalSelectWithEvenNestedNot(CMemoryPool *mp);

	// generate a random select expression with an odd number of nested NOT nodes
	static CExpression *PexprLogicalSelectWithOddNestedNot(CMemoryPool *mp);

	// generate a random select expression with nested AND-OR tree
	static CExpression *PexprLogicalSelectWithNestedAndOrNot(CMemoryPool *mp);

	// generate a Select expression with ANY/ALL subquery predicate over a const table get
	static CExpression *PexprLogicalSubqueryWithConstTableGet(
		CMemoryPool *mp, COperator::EOperatorId op_id);

	// generate a random select expression with constant ANY subquery
	static CExpression *PexprLogicalSelectWithConstAnySubquery(CMemoryPool *mp);

	// generate a random select expression with constant ALL subquery
	static CExpression *PexprLogicalSelectWithConstAllSubquery(CMemoryPool *mp);

	// generate a correlated select expression
	static CExpression *PexprLogicalSelectCorrelated(CMemoryPool *mp);

	// generate a correlated select expression
	static CExpression *PexprLogicalSelectCorrelated(CMemoryPool *mp,
													 CColRefSet *outer_refs,
													 ULONG ulLevel);

	// generate a select on top of outer join expression
	static CExpression *PexprLogicalSelectOnOuterJoin(CMemoryPool *mp);

	// generate a join expression with a random equality predicate
	template <class T>
	static CExpression *PexprLogicalJoin(CMemoryPool *mp,
										 CExpression *pexprLeft,
										 CExpression *pexprRight);

	// generate a random binary join expression of specific join type
	template <class T>
	static CExpression *PexprLogicalJoin(CMemoryPool *mp);

	// generate join with correlation beneath it
	static CExpression *PexprLogicalJoinCorrelated(CMemoryPool *mp);

	// generate join with a partitioned and indexed inner child
	static CExpression *PexprLogicalJoinWithPartitionedAndIndexedInnerChild(
		CMemoryPool *mp);

	// generate a select expression with predicate between a scalar ident and a constant
	static CExpression *PexprLogicalSelectCmpToConst(CMemoryPool *mp);

	// generate a select expression with an array compare
	static CExpression *PexprLogicalSelectArrayCmp(CMemoryPool *mp);

	// generate a select expression with an array compare
	static CExpression *PexprLogicalSelectArrayCmp(
		CMemoryPool *mp, CScalarArrayCmp::EArrCmpType earrcmptype,
		IMDType::ECmpType ecmptype);

	// generate a select expression with an array compare
	static CExpression *PexprLogicalSelectArrayCmp(
		CMemoryPool *mp, CScalarArrayCmp::EArrCmpType earrcmptype,
		IMDType::ECmpType ecmptype, const IntPtrArray *pdrgpiVals);

	// generate an n-ary join expression
	static CExpression *PexprLogicalNAryJoin(CMemoryPool *mp,
											 CExpressionArray *pdrgpexpr);

	// generate an n-ary join expression using given array of relation names
	static CExpression *PexprLogicalNAryJoin(CMemoryPool *mp,
											 CWStringConst *pstrRel,
											 ULONG *pulRel, ULONG ulRels,
											 BOOL fCrossProduct);

	// generate a random n-ary expression
	static CExpression *PexprLogicalNAryJoin(CMemoryPool *mp);

	// generate left outer join on top of n-ary join expression
	static CExpression *PexprLeftOuterJoinOnNAryJoin(CMemoryPool *mp);

	// generate right outer join expression
	static CExpression *PexprRightOuterJoin(CMemoryPool *mp);

	// generate n-ary join on top of left outer join expression
	static CExpression *PexprNAryJoinOnLeftOuterJoin(CMemoryPool *mp);

	// generate a project expression
	static CExpression *PexprLogicalProject(CMemoryPool *mp, CExpression *pexpr,
											CColRef *colref,
											CColRef *new_colref);

	// generate a random project expression
	static CExpression *PexprLogicalProject(CMemoryPool *mp);

	// generate a Project over GbAgg with correlation beneath it
	static CExpression *PexprLogicalProjectGbAggCorrelated(CMemoryPool *mp);

	// generate a limit expression
	static CExpression *PexprLogicalLimit(CMemoryPool *mp, CExpression *pexpr,
										  LINT iStart, LINT iRows, BOOL fGlobal,
										  BOOL fHasCount);

	// generate a random limit expression
	static CExpression *PexprLogicalLimit(CMemoryPool *mp);

	// generate a random group by expression
	static CExpression *PexprLogicalGbAgg(CMemoryPool *mp);

	// generate a random group by expression
	static CExpression *PexprLogicalGbAggWithInput(CMemoryPool *mp,
												   CExpression *pexprInput);

	// generate a random group by with a sum expression
	static CExpression *PexprLogicalGbAggWithSum(CMemoryPool *mp);

	// generate a random group by over join expression
	static CExpression *PexprLogicalGbAggOverJoin(CMemoryPool *mp);

	// generate GbAgg with correlation beneath it
	static CExpression *PexprLogicalGbAggCorrelated(CMemoryPool *mp);

	// generate a dedup group by over inner join expression
	static CExpression *PexprLogicalGbAggDedupOverInnerJoin(CMemoryPool *mp);

	// generate a top-level apply expression
	template <class T>
	static CExpression *PexprLogicalApply(CMemoryPool *mp);

	// generate a top-level apply expression with an outer reference
	template <class T>
	static CExpression *PexprLogicalApplyWithOuterRef(CMemoryPool *mp);

	// generate a const table get expression
	static CExpression *PexprConstTableGet(CMemoryPool *mp, ULONG ulElements);

	// generate a const table get expression with 5 elements
	static CExpression *PexprConstTableGet5(CMemoryPool *mp);

	// generate a logical insert expression
	static CExpression *PexprLogicalInsert(CMemoryPool *mp);

	// generate a logical delete expression
	static CExpression *PexprLogicalDelete(CMemoryPool *mp);

	// generate a logical update expression
	static CExpression *PexprLogicalUpdate(CMemoryPool *mp);

	// generate a dynamic get expression
	static CExpression *PexprLogicalDynamicGet(
		CMemoryPool *mp, CTableDescriptor *ptabdesc,
		const CWStringConst *pstrTableAlias, ULONG ulPartIndex);

	// generate a dynamic get expression
	static CExpression *PexprLogicalDynamicGet(CMemoryPool *mp);

	// generate a select with an equality predicate over a dynamic get expression
	static CExpression *PexprLogicalSelectWithEqPredicateOverDynamicGet(
		CMemoryPool *mp);

	// generate a select with a less than predicate over a dynamic get expression
	static CExpression *PexprLogicalSelectWithLTPredicateOverDynamicGet(
		CMemoryPool *mp);

	// generate a logical table-valued function expression with 2 constant arguments
	static CExpression *PexprLogicalTVFTwoArgs(CMemoryPool *mp);

	// generate a logical table-valued function expression with 3 constant arguments
	static CExpression *PexprLogicalTVFThreeArgs(CMemoryPool *mp);

	// generate a logical table-valued function expression with no arguments
	static CExpression *PexprLogicalTVFNoArgs(CMemoryPool *mp);

	// generate a logical table-valued function expression
	static CExpression *PexprLogicalTVF(CMemoryPool *mp, ULONG ulArgs);

	// generate a cte producer on top of a select
	static CExpression *PexprLogicalCTEProducerOverSelect(CMemoryPool *mp,
														  ULONG ulCTEId);

	// generate a CTE producer on top of a select
	static CExpression *PexprLogicalCTEProducerOverSelect(CMemoryPool *mp);

	// generate an expression with CTE producer and consumer
	static CExpression *PexprCTETree(CMemoryPool *mp);

	// generate a logical sequence expression
	static CExpression *PexprLogicalSequence(CMemoryPool *mp,
											 CExpressionArray *pdrgpexpr);

	// generate a logical sequence expression
	static CExpression *PexprLogicalSequence(CMemoryPool *mp);

	// generate a random union expression
	static CExpression *PexprLogicalUnion(CMemoryPool *mp, ULONG ulDepth);

	// generate a sequence project expression
	static CExpression *PexprLogicalSequenceProject(CMemoryPool *mp,
													OID oidFunc,
													CExpression *pexprInput);

	// generate a random expression with one window function
	static CExpression *PexprOneWindowFunction(CMemoryPool *mp);

	// generate a random expression with two window functions
	static CExpression *PexprTwoWindowFunctions(CMemoryPool *mp);

	// generate a dynamic array of join expressions
	static CExpressionJoinsArray *PdrgpexprJoins(CMemoryPool *mp,
												 CWStringConst *pstrRel,
												 ULONG *pulRel, ULONG ulRels,
												 BOOL fCrossProduct);

	// generate a query context from an array of required column references
	static CQueryContext *PqcGenerate(CMemoryPool *mp, CExpression *pexpr,
									  CColRefArray *colref_array);

	// generate a dummy query context for testing
	static CQueryContext *PqcGenerate(CMemoryPool *mp, CExpression *pexpr);

	// generate a nested AND/OR tree
	static CExpression *PexprScalarNestedPreds(
		CMemoryPool *mp, CExpression *pexpr,
		CScalarBoolOp::EBoolOperator eboolop);

	// find the first child expression with the given operator id
	static CExpression *PexprFindFirstExpressionWithOpId(
		CExpression *pexpr, COperator::EOperatorId op_id);

	// equality predicate shortcut
	static void EqualityPredicate(CMemoryPool *mp, CColRefSet *pcrsLeft,
								  CColRefSet *pcrsRight,
								  CExpressionArray *pdrgpexpr);

	// int2 point
	static CPoint *PpointInt2(CMemoryPool *mp, INT i);

	// int4 point
	static CPoint *PpointInt4(CMemoryPool *mp, INT i);

	// create an INT4 point with null value
	static CPoint *PpointInt4NullVal(CMemoryPool *mp);

	// int8 point
	static CPoint *PpointInt8(CMemoryPool *mp, INT li);

	// bool point
	static CPoint *PpointBool(CMemoryPool *mp, BOOL value);

	// perform DXL(query) -> CExpression(Log) ->CExpression(Ph) -> DXL(plan)
	// and check whether the resulting plan DXL matches the expected output
	static GPOS_RESULT EresTranslate(CMemoryPool *mp,
									 const CHAR *szQueryFileName,
									 const CHAR *szPlanFileName,
									 BOOL fIgnoreMismatch);

	// return query logical expression from minidump
	static CExpression *PexprReadQuery(CMemoryPool *mp,
									   const CHAR *szQueryFileName);

	// compare expected and actual output
	static GPOS_RESULT EresCompare(IOstream &os, CWStringDynamic *pstrActual,
								   CWStringDynamic *pstrExpected,
								   BOOL fIgnoreMismatch = true);

	static CHAR *ExtractFilenameFromPath(CHAR *file_path);

	static void CreateExpectedAndActualFile(CMemoryPool *mp,
											const CHAR *file_name,
											CWStringDynamic *strExpected,
											CWStringDynamic *strActual);

	// match given two plans using string comparison
	static BOOL FPlanMatch(CMemoryPool *mp, IOstream &os,
						   const CDXLNode *first_child_dxlnode,
						   ULLONG ullPlanIdFst, ULLONG ullPlanSpaceSizeFst,
						   const CDXLNode *second_child_dxlnode,
						   ULLONG ullPlanIdSnd, ULLONG ullPlanSpaceSizeSnd,
						   const CHAR *file_name);

	// compare given DXL plans
	static BOOL FPlanCompare(CMemoryPool *mp, IOstream &os,
							 const CDXLNode *first_child_dxlnode,
							 ULLONG ullPlanIdFst, ULLONG ullPlanSpaceSizeFst,
							 const CDXLNode *second_child_dxlnode,
							 ULLONG ullPlanIdSnd, ULLONG ullPlanSpaceSizeSnd,
							 BOOL fMatchPlans, INT iCmpSpaceSize,
							 const CHAR *file_name);

	// run minidumps while loading metadata from passed file
	static GPOS_RESULT EresRunMinidumps(CMemoryPool *mp,
										const CHAR *rgszFileNames[],
										ULONG ulTests, ULONG *pulTestCounter,
										ULONG ulSessionId, ULONG ulCmdId,
										BOOL fMatchPlans,
										BOOL fTestSpacePruning,
										const CHAR *szMDFilePath = nullptr,
										IConstExprEvaluator *pceeval = nullptr);

	// run all minidumps based on one metadata file
	static GPOS_RESULT EresRunMinidumpsUsingOneMDFile(
		CMemoryPool *mp, const CHAR *szMDFilePath, const CHAR *rgszFileNames[],
		ULONG *pulTestCounter, ULONG ulSessionId, ULONG ulCmdId,
		BOOL fMatchPlans, INT iCmpSpaceSize,
		IConstExprEvaluator *pceeval = nullptr);

	// run one minidump-based test using passed MD Accessor
	static GPOS_RESULT EresRunMinidump(
		CMemoryPool *mp, CMDAccessor *md_accessor, const CHAR *rgszFileName,
		ULONG *pulTestCounter, ULONG ulSessionId, ULONG ulCmdId,
		BOOL fMatchPlans, INT iCmpSpaceSize, IConstExprEvaluator *pceeval);

	// test plan sampling
	static GPOS_RESULT EresSamplePlans(const CHAR *rgszFileNames[],
									   ULONG ulTests, ULONG *pulTestCounter,
									   ULONG ulSessionId, ULONG ulCmdId);

	// check all enumerated plans using given PlanChecker function
	static GPOS_RESULT EresCheckPlans(const CHAR *rgszFileNames[],
									  ULONG ulTests, ULONG *pulTestCounter,
									  ULONG ulSessionId, ULONG ulCmdId,
									  FnPlanChecker *pfpc);

	// check the optimized plan using given DXLPlanChecker function
	// cost model must be calibrated
	// does not take ownership of pdrgpcp
	static GPOS_RESULT EresCheckOptimizedPlan(const CHAR *rgszFileNames[],
											  ULONG ulTests,
											  ULONG *pulTestCounter,
											  ULONG ulSessionId, ULONG ulCmdId,
											  FnDXLPlanChecker *pfdpc,
											  ICostModelParamsArray *pdrgpcp);

	// generate cost model used in tests
	static ICostModel *
	GetCostModel(CMemoryPool *mp)
	{
		return GPOS_NEW(mp) CCostModelGPDB(mp, GPOPT_TEST_SEGMENTS);
	}

	// create a datum with a given type, encoded value and int value
	static IDatum *CreateGenericDatum(CMemoryPool *mp, CMDAccessor *md_accessor,
									  IMDId *mdid_type,
									  CWStringDynamic *pstrEncodedValue,
									  LINT value);

	// Create a datum with a given type and double value
	static IDatum *CreateDoubleDatum(CMemoryPool *mp, CMDAccessor *md_accessor,
									 IMDId *mdid_type, CDouble value);
	// create an interval for generic data types
	// does not take ownership of mdid_type
	static CConstraintInterval *PciGenericInterval(
		CMemoryPool *mp, CMDAccessor *md_accessor, const CMDIdGPDB &mdidType,
		CColRef *colref, CWStringDynamic *pstrLower, LINT lLower,
		CRange::ERangeInclusion eriLeft, CWStringDynamic *pstrUpper,
		LINT lUpper, CRange::ERangeInclusion eriRight);

	// helper for generating a scalar compare ident to constant
	static CExpression *PexprScalarCmpIdentToConstant(CMemoryPool *mp,
													  CExpression *pexpr);

	// helper for generating an exists subquery
	static CExpression *PexprExistsSubquery(CMemoryPool *mp,
											CExpression *pexprOuter);

	// helper for generating a not exists subquery
	static CExpression *PexprNotExistsSubquery(CMemoryPool *mp,
											   CExpression *pexprOuter);

	// helper for generating an ANY subquery
	static CExpression *PexpSubqueryAny(CMemoryPool *mp,
										CExpression *pexprOuter);

	// helper for generating an ALL subquery
	static CExpression *PexpSubqueryAll(CMemoryPool *mp,
										CExpression *pexprOuter);

	// recursively traverses the subtree rooted at the given expression, and return
	// the first subexpression it encounters that has the given id
	static const CExpression *PexprFirst(const CExpression *pexpr,
										 const COperator::EOperatorId op_id);

	// generate a scalar expression comparing scalar identifier to a constant
	static CExpression *PexprScIdentCmpConst(CMemoryPool *mp,
											 CExpression *pexprRelational,
											 IMDType::ECmpType cmp_type,
											 ULONG ulVal);

	// generate a scalar expression comparing scalar identifiers
	static CExpression *PexprScIdentCmpScIdent(CMemoryPool *mp,
											   CExpression *pexprLeft,
											   CExpression *pexprRight,
											   IMDType::ECmpType cmp_type);

	// generate a scalar AND expression
	static CExpression *PexprAnd(CMemoryPool *mp, CExpression *pexprFst,
								 CExpression *pexprSnd);

	// generate a scalar OR expression
	static CExpression *PexprOr(CMemoryPool *mp, CExpression *pexprFst,
								CExpression *pexprSnd);

	// default file-based metadata provider
	static CMDProviderMemory *m_pmdpf;

	// default source system id
	static CSystemId m_sysidDefault;

	// metadata file
	static const CHAR *m_szMDFileName;

	// xsd file
	static const CHAR *m_szXSDPath;

	// run Minidump-based tests in the given array of files
	static GPOS_RESULT EresUnittest_RunTests(const CHAR **rgszFileNames,
											 ULONG *pulTestCounter,
											 ULONG ulTests);

	static GPOS_RESULT EresUnittest_RunTestsWithoutAdditionalTraceFlags(
		const CHAR **rgszFileNames, ULONG *pulTestCounter, ULONG ulTests,
		BOOL fMatchPlans, BOOL fTestSpacePruning);

	// return the number of segments, default return GPOPT_TEST_SEGMENTS
	static ULONG UlSegments(COptimizerConfig *optimizer_config);

	// create Equivalence Class based on the breakpoints
	static CColRefSetArray *createEquivalenceClasses(CMemoryPool *mp,
													 CColRefSet *pcrs,
													 const INT setBoundary[]);
};	// class CTestUtils


//---------------------------------------------------------------------------
//		CTestUtils::PexprJoinPartitionedOuter
//
//	@doc:
//		Generate a randomized join expression over a partitioned table
//
//---------------------------------------------------------------------------
template <class T>
CExpression *
CTestUtils::PexprJoinPartitionedOuter(CMemoryPool *mp)
{
	return PexprJoinPartitioned<T>(mp, true /*fOuterPartitioned*/);
}

//---------------------------------------------------------------------------
//		CTestUtils::PexprJoinPartitionedInner
//
//	@doc:
//		Generate a randomized join expression over a partitioned table
//
//---------------------------------------------------------------------------
template <class T>
CExpression *
CTestUtils::PexprJoinPartitionedInner(CMemoryPool *mp)
{
	return PexprJoinPartitioned<T>(mp, false /*fOuterPartitioned*/);
}

//---------------------------------------------------------------------------
//		CTestUtils::PexprJoinPartitioned
//
//	@doc:
//		Generate a randomized join expression over a partitioned table
//
//---------------------------------------------------------------------------
template <class T>
CExpression *
CTestUtils::PexprJoinPartitioned(CMemoryPool *mp, BOOL fOuterPartitioned)
{
	// create an outer Get expression and extract a random column from it
	CExpression *pexprGet = PexprLogicalGet(mp);
	CColRef *colref = pexprGet->DeriveOutputColumns()->PcrAny();

	// create an inner partitioned Get expression
	CExpression *pexprGetPartitioned = PexprLogicalGetPartitioned(mp);

	// extract first partition key
	CLogicalGet *popGetPartitioned =
		CLogicalGet::PopConvert(pexprGetPartitioned->Pop());
	const CColRef2dArray *pdrgpdrgpcr =
		popGetPartitioned->PdrgpdrgpcrPartColumns();

	GPOS_ASSERT(pdrgpdrgpcr != nullptr);
	GPOS_ASSERT(0 < pdrgpdrgpcr->Size());
	CColRefArray *colref_array = (*pdrgpdrgpcr)[0];
	GPOS_ASSERT(1 == colref_array->Size());
	CColRef *pcrPartKey = (*colref_array)[0];

	// construct a comparison pk = a
	CExpression *pexprScalar =
		CUtils::PexprScalarEqCmp(mp, CUtils::PexprScalarIdent(mp, colref),
								 CUtils::PexprScalarIdent(mp, pcrPartKey));

	if (fOuterPartitioned)
	{
		return CUtils::PexprLogicalJoin<T>(mp, pexprGetPartitioned, pexprGet,
										   pexprScalar);
	}

	return CUtils::PexprLogicalJoin<T>(mp, pexprGet, pexprGetPartitioned,
									   pexprScalar);
}

//---------------------------------------------------------------------------
//	@function:
//		CTestUtils::PexprLogicalJoin
//
//	@doc:
//		Generate a join expression with a random equality predicate
//
//---------------------------------------------------------------------------
template <class T>
CExpression *
CTestUtils::PexprLogicalJoin(CMemoryPool *mp, CExpression *pexprLeft,
							 CExpression *pexprRight)
{
	GPOS_ASSERT(nullptr != pexprLeft);
	GPOS_ASSERT(nullptr != pexprRight);

	// get any two columns; one from each side
	CColRef *pcrLeft = pexprLeft->DeriveOutputColumns()->PcrAny();
	CColRef *pcrRight = pexprRight->DeriveOutputColumns()->PcrAny();
	CExpression *pexprEquality =
		CUtils::PexprScalarEqCmp(mp, pcrLeft, pcrRight);

	return CUtils::PexprLogicalJoin<T>(mp, pexprLeft, pexprRight,
									   pexprEquality);
}

//---------------------------------------------------------------------------
//	@function:
//		CTestUtils::PexprLogicalJoin
//
//	@doc:
//		Generate randomized basic join expression
//
//---------------------------------------------------------------------------
template <class T>
CExpression *
CTestUtils::PexprLogicalJoin(CMemoryPool *mp)
{
	CExpression *pexprLeft = PexprLogicalGet(mp);
	CExpression *pexprRight = PexprLogicalGet(mp);
	// reversing the order here is a workaround for non-determinism
	// in CExpressionPreprocessorTest where join order was flipped
	// depending on the compiler used
	return PexprLogicalJoin<T>(mp, pexprRight, pexprLeft);
}

//---------------------------------------------------------------------------
//	@function:
//		CTestUtils::PexprLogicalApply
//
//	@doc:
//		Generate top-level apply
//
//---------------------------------------------------------------------------
template <class T>
CExpression *
CTestUtils::PexprLogicalApply(CMemoryPool *mp)
{
	CExpression *pexprOuter = PexprLogicalGet(mp);
	CColRefSet *outer_refs = pexprOuter->DeriveOutputColumns();

	CExpression *pexprInner = PexprLogicalSelectCorrelated(mp, outer_refs, 3);
	CExpression *pexprPredicate =
		CUtils::PexprScalarConstBool(mp, true /*value*/);

	COperator *pop = GPOS_NEW(mp) T(mp);
	return GPOS_NEW(mp)
		CExpression(mp, pop, pexprOuter, pexprInner, pexprPredicate);
}

//---------------------------------------------------------------------------
//	@function:
//		CTestUtils::PexprLogicalApplyWithOuterRef
//
//	@doc:
//		Generate top-level apply
//
//---------------------------------------------------------------------------
template <class T>
CExpression *
CTestUtils::PexprLogicalApplyWithOuterRef(CMemoryPool *mp)
{
	CExpression *pexprOuter = PexprLogicalGet(mp);
	CColRefSet *outer_refs = pexprOuter->DeriveOutputColumns();

	CExpression *pexprInner = PexprLogicalSelectCorrelated(mp, outer_refs, 3);
	CExpression *pexprPredicate =
		CUtils::PexprScalarConstBool(mp, true /*value*/);

	CColRefSet *pcrsOuterRef = pexprInner->DeriveOuterReferences();
	GPOS_ASSERT(1 == pcrsOuterRef->Size());
	CColRef *colref = pcrsOuterRef->PcrFirst();

	CColRefArray *colref_array = GPOS_NEW(mp) CColRefArray(mp);
	colref_array->Append(colref);
	COperator *pop =
		GPOS_NEW(mp) T(mp, colref_array, COperator::EopScalarSubquery);
	return GPOS_NEW(mp)
		CExpression(mp, pop, pexprOuter, pexprInner, pexprPredicate);
}

}  // namespace gpopt


#endif	// !GPOPT_CTestUtils_H

// EOF
