greenplumn CEngineTest 源码

2022-08-18 浏览 (267)

greenplumn CEngineTest 代码

文件路径:/src/backend/gporca/server/src/unittest/gpopt/engine/CEngineTest.cpp

/---------------------------------------------------------------------------
/	Greenplum Database
/	Copyright (C) 2009 - 2011 EMC Corp.
/
/	@filename:
/		CEngineTest.cpp
/
/	@doc:
/		Test for CEngine
/---------------------------------------------------------------------------
#include "unittest/gpopt/engine/CEngineTest.h"

#include "gpos/error/CAutoTrace.h"
#include "gpos/task/CAutoTraceFlag.h"

#include "gpopt/base/CColRefSetIter.h"
#include "gpopt/base/CUtils.h"
#include "gpopt/engine/CEngine.h"
#include "gpopt/eval/CConstExprEvaluatorDefault.h"
#include "gpopt/mdcache/CMDCache.h"
#include "gpopt/operators/CLogicalInnerJoin.h"
#include "gpopt/search/CGroup.h"
#include "gpopt/search/CGroupProxy.h"

#include "unittest/base.h"
#include "unittest/gpopt/CSubqueryTestUtils.h"

ULONG CEngineTest::m_ulTestCounter = 0;		 / start from first test
ULONG CEngineTest::m_ulTestCounterSubq = 0;	 / start from first test

/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest
/
/	@doc:
/		Unittest for engine
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest()
{
	CUnittest rgut[] = {
		GPOS_UNITTEST_FUNC(EresUnittest_Basic),
#ifdef GPOS_DEBUG
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemo),
		GPOS_UNITTEST_FUNC(EresUnittest_AppendStats),
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemoWithSubqueries),
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemoWithGrouping),
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemoWithTVF),
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemoWithPartitioning),
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemoWithWindowing),
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemoLargeJoins),
		GPOS_UNITTEST_FUNC(EresUnittest_BuildMemoWithCTE),
#endif	/ GPOS_DEBUG
	};

	return CUnittest::EresExecute(rgut, GPOS_ARRAY_SIZE(rgut));
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_Basic
/
/	@doc:
/		Basic test
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_Basic()
{
	CAutoMemoryPool amp;
	CMemoryPool *mp = amp.Pmp();

	CAutoTraceFlag atf1(EopttracePrintOptimizationStatistics, true);
	CAutoTraceFlag atf2(EopttracePrintMemoAfterExploration, true);
	CAutoTraceFlag atf3(EopttracePrintMemoAfterImplementation, true);
	CAutoTraceFlag atf4(EopttracePrintMemoAfterOptimization, true);
	CAutoTraceFlag atf6(EopttracePrintXform, true);
	CAutoTraceFlag atf7(EopttracePrintGroupProperties, true);
	CAutoTraceFlag atf8(EopttracePrintOptimizationContext, true);
	CAutoTraceFlag atf9(EopttracePrintXformPattern, true);

	/ setup a file-based provider
	CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
	pmdp->AddRef();
	CMDAccessor mda(mp, CMDCache::Pcache(), CTestUtils::m_sysidDefault, pmdp);

	/ install opt context in TLS
	CAutoOptCtxt aoc(mp, &mda, nullptr, /* pceeval */
					 CTestUtils::GetCostModel(mp));

	CEngine eng(mp);

	/ generate  join expression
	CExpression *pexpr = CTestUtils::PexprLogicalJoin<CLogicalInnerJoin>(mp);

	/ generate query context
	CQueryContext *pqc = CTestUtils::PqcGenerate(mp, pexpr);

	/ Initialize engine
	eng.Init(pqc, nullptr /*search_stage_array*/);

	/ optimize query
	eng.Optimize();

	/ extract plan
	CExpression *pexprPlan = eng.PexprExtractPlan();
	GPOS_ASSERT(nullptr != pexprPlan);

	/ clean up
	pexpr->Release();
	pexprPlan->Release();
	GPOS_DELETE(pqc);

	return GPOS_OK;
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresOptimize
/
/	@doc:
/		Helper for optimizing deep join trees;
/		generate an array of join expressions for the given relation, if the
/		bit corresponding to an expression is set, the optimizer is invoked
/		for that expression
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresOptimize(
	FnOptimize *pfopt,
	CWStringConst *str,	 / array of relation names
	ULONG *pul,			 / array of relation OIDs
	ULONG ulRels,		 / number of array entries
	CBitSet *
		pbs	 / if a bit is set, the corresponding join expression will be optimized
)
{
	GPOS_ASSERT(nullptr != pfopt);
	GPOS_ASSERT(nullptr != str);
	GPOS_ASSERT(nullptr != pul);
	GPOS_ASSERT(nullptr != pbs);

	CAutoMemoryPool amp;
	CMemoryPool *mp = amp.Pmp();

	/ setup a file-based provider
	CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
	pmdp->AddRef();
	CMDAccessor mda(mp, CMDCache::Pcache());
	mda.RegisterProvider(CTestUtils::m_sysidDefault, pmdp);
	/ scope for optimization context
	{
		CAutoOptCtxt aoc(mp, &mda, nullptr, /* pceeval */
						 CTestUtils::GetCostModel(mp));

		/ generate cross product expressions
		CExpressionJoinsArray *pdrgpexprCrossProducts =
			CTestUtils::PdrgpexprJoins(mp, str, pul, ulRels,
									   true /*fCrossProduct*/);

		/ generate join expressions
		CExpressionJoinsArray *pdrgpexpr = CTestUtils::PdrgpexprJoins(
			mp, str, pul, ulRels, false /*fCrossProduct*/);

		/ build memo for each expression
		for (ULONG ul = m_ulTestCounter; ul < ulRels; ul++)
		{
			if (pbs->Get(ul))
			{
				pfopt(mp, (*pdrgpexprCrossProducts)[ul],
					  nullptr /*search_stage_array*/);
				GPOS_CHECK_ABORT;

				pfopt(mp, (*pdrgpexpr)[ul], nullptr /*search_stage_array*/);
				GPOS_CHECK_ABORT;

				m_ulTestCounter++;
			}
		}

		/ reset counter
		m_ulTestCounter = 0;

		(*pdrgpexprCrossProducts)[ulRels - 1]->Release();
		CRefCount::SafeRelease(pdrgpexprCrossProducts);

		(*pdrgpexpr)[ulRels - 1]->Release();
		CRefCount::SafeRelease(pdrgpexpr);
	}

	return GPOS_OK;
}


#ifdef GPOS_DEBUG
/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemo
/
/	@doc:
/		Test of building memo from different input expressions
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemo()
{
	CAutoMemoryPool amp;
	CMemoryPool *mp = amp.Pmp();

	/ array of relation names
	CWStringConst rgscRel[] = {
		GPOS_WSZ_LIT("Rel1"), GPOS_WSZ_LIT("Rel2"), GPOS_WSZ_LIT("Rel3"),
		GPOS_WSZ_LIT("Rel4"), GPOS_WSZ_LIT("Rel5"),
	};

	/ array of relation IDs
	ULONG rgulRel[] = {
		GPOPT_TEST_REL_OID1, GPOPT_TEST_REL_OID2, GPOPT_TEST_REL_OID3,
		GPOPT_TEST_REL_OID4, GPOPT_TEST_REL_OID5,
	};

	CBitSet *pbs = GPOS_NEW(mp) CBitSet(mp);
	const ULONG ulRels = GPOS_ARRAY_SIZE(rgscRel);
	for (ULONG ul = 0; ul < ulRels; ul++)
	{
		(void) pbs->ExchangeSet(ul);
	}

	GPOS_RESULT eres =
		EresOptimize(BuildMemoRecursive, rgscRel, rgulRel, ulRels, pbs);
	pbs->Release();

	return eres;
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_AppendStats
/
/	@doc:
/		Test of appending stats during optimization
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_AppendStats()
{
	CAutoMemoryPool amp;
	CMemoryPool *mp = amp.Pmp();

	CAutoTraceFlag atf(EopttracePrintGroupProperties, true);

	/ setup a file-based provider
	CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
	pmdp->AddRef();
	CMDAccessor mda(mp, CMDCache::Pcache(), CTestUtils::m_sysidDefault, pmdp);

	/ install opt context in TLS
	CAutoOptCtxt aoc(mp, &mda, nullptr, /* pceeval */
					 CTestUtils::GetCostModel(mp));

	CEngine eng(mp);

	/ generate  join expression
	CExpression *pexpr = CTestUtils::PexprLogicalJoin<CLogicalInnerJoin>(mp);

	/ generate query context
	CQueryContext *pqc = CTestUtils::PqcGenerate(mp, pexpr);

	/ Initialize engine
	eng.Init(pqc, nullptr /*search_stage_array*/);

	CGroupExpression *pgexpr = nullptr;
	{
		CGroupProxy gp(eng.PgroupRoot());
		pgexpr = gp.PgexprFirst();
	}

	/ derive stats with empty requirements
	{
		CAutoTrace at(mp);

		CExpressionHandle exprhdl(mp);
		exprhdl.Attach(pgexpr);
		exprhdl.DeriveStats(mp, mp, nullptr /*prprel*/, nullptr /*stats_ctxt*/);
		at.Os() << std::endl
				<< "MEMO AFTER FIRST STATS DERIVATION:" << std::endl;
	}
	eng.Trace();

	/ create a non-empty set of output columns as requirements for stats derivation
	ULONG ulIndex = 0;
	CColRefSet *pcrs = GPOS_NEW(mp) CColRefSet(mp);
	CColRefSetIter crsi(*pexpr->DeriveOutputColumns());
	while (crsi.Advance() && ulIndex < 3)
	{
		CColRef *colref = crsi.Pcr();
		if (ulIndex == 1 || ulIndex == 2)
		{
			pcrs->Include(colref);
		}
		ulIndex++;
	}

	CReqdPropRelational *prprel = GPOS_NEW(mp) CReqdPropRelational(pcrs);

	/ derive stats with non-empty requirements
	/ missing stats should be appended to the already derived ones
	{
		CAutoTrace at(mp);

		CExpressionHandle exprhdl(mp);
		exprhdl.Attach(pgexpr);
		exprhdl.DeriveStats(mp, mp, prprel, nullptr /*stats_ctxt*/);
		at.Os() << std::endl
				<< "MEMO AFTER SECOND STATS DERIVATION:" << std::endl;
	}
	eng.Trace();

	pexpr->Release();
	prprel->Release();
	GPOS_DELETE(pqc);

	return GPOS_OK;
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemoLargeJoins
/
/	@doc:
/		Test of building memo for a large number of joins
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemoLargeJoins()
{
	CAutoMemoryPool amp;
	CMemoryPool *mp = amp.Pmp();

	/ array of relation names
	CWStringConst rgscRel[] = {
		GPOS_WSZ_LIT("Rel1"), GPOS_WSZ_LIT("Rel2"), GPOS_WSZ_LIT("Rel3"),
		GPOS_WSZ_LIT("Rel4"), GPOS_WSZ_LIT("Rel5"),
	};

	/ array of relation IDs
	ULONG rgulRel[] = {
		GPOPT_TEST_REL_OID1, GPOPT_TEST_REL_OID2, GPOPT_TEST_REL_OID3,
		GPOPT_TEST_REL_OID4, GPOPT_TEST_REL_OID5,
	};

	/ only optimize the last join expression
	CBitSet *pbs = GPOS_NEW(mp) CBitSet(mp);
	const ULONG ulRels = GPOS_ARRAY_SIZE(rgscRel);
	(void) pbs->ExchangeSet(ulRels - 1);

	GPOS_RESULT eres =
		EresOptimize(BuildMemoRecursive, rgscRel, rgulRel, ulRels, pbs);
	pbs->Release();

	return eres;
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::BuildMemoRecursive
/
/	@doc:
/		Build memo recursively from a given input expression
/
/---------------------------------------------------------------------------
void
CEngineTest::BuildMemoRecursive(CMemoryPool *mp, CExpression *pexprInput,
								CSearchStageArray *search_stage_array)
{
	CQueryContext *pqc = CTestUtils::PqcGenerate(mp, pexprInput);

	CAutoTrace at(mp);
	IOstream &os(at.Os());

	os << std::endl << std::endl;
	os << "QUERY CONTEXT:" << std::endl;
	(void) pqc->OsPrint(os);

	/ enable space pruning
	CAutoTraceFlag atf(EopttraceEnableSpacePruning, true /*value*/);

	CEngine eng(mp);
	eng.Init(pqc, search_stage_array);
	eng.PrintRoot();
	GPOS_CHECK_ABORT;

	eng.RecursiveOptimize();
	GPOS_CHECK_ABORT;

	CExpression *pexprPlan = eng.PexprExtractPlan();
	GPOS_ASSERT(nullptr != pexprPlan);

	os << std::endl << std::endl;
	os << "OUTPUT PLAN:" << std::endl;
	(void) pexprPlan->OsPrint(os);
	os << std::endl << std::endl;

	eng.PrintOptCtxts();
	pexprPlan->Release();
	GPOS_DELETE(pqc);

	GPOS_CHECK_ABORT;
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresTestEngine
/
/	@doc:
/		Helper for testing engine using an array of expression generators
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresTestEngine(Pfpexpr rgpf[], ULONG size)
{
	CAutoMemoryPool amp;
	CMemoryPool *mp = amp.Pmp();

	/ setup a file-based provider
	CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
	pmdp->AddRef();
	CMDAccessor mda(mp, CMDCache::Pcache());
	mda.RegisterProvider(CTestUtils::m_sysidDefault, pmdp);

	for (ULONG ul = m_ulTestCounter; ul < size; ul++)
	{
		/ install opt context in TLS
		CAutoOptCtxt aoc(mp, &mda, nullptr, /* pceeval */
						 CTestUtils::GetCostModel(mp));

		CExpression *pexpr = rgpf[ul](mp);
		BuildMemoRecursive(mp, pexpr, nullptr /*search_stage_array*/);
		pexpr->Release();

		m_ulTestCounter++;
	}

	/ reset counter
	m_ulTestCounter = 0;

	return GPOS_OK;
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemoWithSubqueries
/
/	@doc:
/		Test of building memo for expressions with subqueries
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemoWithSubqueries()
{
	using Pfpexpr = CExpression *(*) (CMemoryPool *, BOOL);
	Pfpexpr rgpf[] = {
		CSubqueryTestUtils::PexprSelectWithAllAggSubquery,
		CSubqueryTestUtils::PexprSelectWithAggSubquery,
		CSubqueryTestUtils::PexprSelectWithAggSubqueryConstComparison,
		CSubqueryTestUtils::PexprProjectWithAggSubquery,
		CSubqueryTestUtils::PexprSelectWithAnySubquery,
		CSubqueryTestUtils::PexprProjectWithAnySubquery,
		CSubqueryTestUtils::PexprSelectWithAllSubquery,
		CSubqueryTestUtils::PexprProjectWithAllSubquery,
		CSubqueryTestUtils::PexprSelectWithExistsSubquery,
		CSubqueryTestUtils::PexprProjectWithExistsSubquery,
		CSubqueryTestUtils::PexprSelectWithNotExistsSubquery,
		CSubqueryTestUtils::PexprProjectWithNotExistsSubquery,
		CSubqueryTestUtils::PexprSelectWithNestedCmpSubquery,
		CSubqueryTestUtils::PexprSelectWithCmpSubqueries,
		CSubqueryTestUtils::PexprSelectWithSubqueryConjuncts,
		CSubqueryTestUtils::PexprProjectWithSubqueries,
		CSubqueryTestUtils::PexprSelectWithAggSubqueryOverJoin,
		CSubqueryTestUtils::PexprSelectWithNestedSubquery,
		CSubqueryTestUtils::PexprSelectWithNestedAnySubqueries,
		CSubqueryTestUtils::PexprSelectWithNestedAllSubqueries,
		CSubqueryTestUtils::PexprSelectWith2LevelsCorrSubquery,
		CSubqueryTestUtils::PexprSubqueriesInNullTestContext,
		CSubqueryTestUtils::PexprSubqueriesInDifferentContexts,
		CSubqueryTestUtils::PexprSelectWithTrimmableExists,
		CSubqueryTestUtils::PexprSelectWithTrimmableNotExists,
		CSubqueryTestUtils::PexprSelectWithSubqueryDisjuncts,
		CSubqueryTestUtils::PexprUndecorrelatableAnySubquery,
		CSubqueryTestUtils::PexprUndecorrelatableAllSubquery,
		CSubqueryTestUtils::PexprUndecorrelatableExistsSubquery,
		CSubqueryTestUtils::PexprUndecorrelatableNotExistsSubquery,
		CSubqueryTestUtils::PexprUndecorrelatableScalarSubquery,
		CSubqueryTestUtils::PexprSelectWithAnySubqueryOverWindow,
		CSubqueryTestUtils::PexprSelectWithAllSubqueryOverWindow,
	};

	BOOL fCorrelated = true;

	/ we get two expressions using each generator
	const ULONG size = 2 * GPOS_ARRAY_SIZE(rgpf);
	for (ULONG ul = m_ulTestCounterSubq; ul < size; ul++)
	{
		CAutoMemoryPool amp;
		CMemoryPool *mp = amp.Pmp();

		/ setup a file-based provider
		CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
		pmdp->AddRef();
		CMDAccessor mda(mp, CMDCache::Pcache());
		mda.RegisterProvider(CTestUtils::m_sysidDefault, pmdp);

		{
			/ install opt context in TLS
			CAutoOptCtxt aoc(mp, &mda, nullptr, /* pceeval */
							 CTestUtils::GetCostModel(mp));

			ULONG ulIndex = ul / 2;
			CExpression *pexpr = rgpf[ulIndex](mp, fCorrelated);
			BuildMemoRecursive(mp, pexpr, nullptr /*search_stage_array*/);
			pexpr->Release();
		}

		fCorrelated = !fCorrelated;
		m_ulTestCounterSubq++;
	}

	m_ulTestCounterSubq = 0;

	return GPOS_OK;
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemoWithTVF
/
/	@doc:
/		Test of building memo for expressions with table-valued functions
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemoWithTVF()
{
	Pfpexpr rgpf[] = {
		CTestUtils::PexprLogicalTVFTwoArgs,
		CTestUtils::PexprLogicalTVFThreeArgs,
		CTestUtils::PexprLogicalTVFNoArgs,
	};

	return EresTestEngine(rgpf, GPOS_ARRAY_SIZE(rgpf));
}

/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemoWithGrouping
/
/	@doc:
/		Test of building memo for expressions with grouping
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemoWithGrouping()
{
	Pfpexpr rgpf[] = {
		CTestUtils::PexprLogicalGbAgg, CTestUtils::PexprLogicalGbAggOverJoin,
		CTestUtils::PexprLogicalGbAggWithSum, CTestUtils::PexprLogicalNAryJoin};

	return EresTestEngine(rgpf, GPOS_ARRAY_SIZE(rgpf));
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemoWithPartitioning
/
/	@doc:
/		Test of building memo for expressions with partitioning
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemoWithPartitioning()
{
	Pfpexpr rgpf[] = {
		CTestUtils::PexprLogicalDynamicGet,
		CTestUtils::PexprLogicalSelectWithEqPredicateOverDynamicGet,
		CTestUtils::PexprLogicalSelectWithLTPredicateOverDynamicGet,
		/		CTestUtils::PexprJoinPartitionedOuter<CLogicalInnerJoin>,
		/		CTestUtils::Pexpr3WayJoinPartitioned,
		/		CTestUtils::Pexpr4WayJoinPartitioned
	};

	return EresTestEngine(rgpf, GPOS_ARRAY_SIZE(rgpf));
}


/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemoWithWindowing
/
/	@doc:
/		Test of building memo for expressions with windowing operations
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemoWithWindowing()
{
	Pfpexpr rgpf[] = {
		CTestUtils::PexprOneWindowFunction,
		CTestUtils::PexprTwoWindowFunctions,
	};

	return EresTestEngine(rgpf, GPOS_ARRAY_SIZE(rgpf));
}

/---------------------------------------------------------------------------
/	@function:
/		CEngineTest::EresUnittest_BuildMemoWithCTE
/
/	@doc:
/		Test of building memo for expressions with CTEs
/
/---------------------------------------------------------------------------
GPOS_RESULT
CEngineTest::EresUnittest_BuildMemoWithCTE()
{
	CAutoMemoryPool amp;
	CMemoryPool *mp = amp.Pmp();

	/ setup a file-based provider
	CMDProviderMemory *pmdp = CTestUtils::m_pmdpf;
	pmdp->AddRef();
	CMDAccessor mda(mp, CMDCache::Pcache());
	mda.RegisterProvider(CTestUtils::m_sysidDefault, pmdp);

	/ install opt context in TLS
	CAutoOptCtxt aoc(mp, &mda, nullptr, /* pceeval */
					 CTestUtils::GetCostModel(mp));

	CExpression *pexprCTE = CTestUtils::PexprCTETree(mp);
	CExpression *pexprGet = CTestUtils::PexprLogicalGet(mp);

	CColRefSet *pcrsLeft = pexprCTE->DeriveOutputColumns();
	CColRef *pcrLeft = pcrsLeft->PcrAny();

	CColRefSet *pcrsRight = pexprGet->DeriveOutputColumns();
	CColRef *pcrRight = pcrsRight->PcrAny();

	CExpression *pexprScalar = CUtils::PexprScalarEqCmp(mp, pcrLeft, pcrRight);

	CExpression *pexpr =
		GPOS_NEW(mp) CExpression(mp, GPOS_NEW(mp) CLogicalInnerJoin(mp),
								 pexprCTE, pexprGet, pexprScalar);

	BuildMemoRecursive(mp, pexpr, nullptr /*search_stage_array*/);
	pexpr->Release();

	return GPOS_OK;
}

#endif	/ GPOS_DEBUG

/ EOF

相关信息

greenplumn 源码目录

相关文章

greenplumn CBindingTest 源码

greenplumn CEnumeratorTest 源码

^