blob: a49f248091b7e52430c1ef49887d636eed461a88 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
//---------------------------------------------------------------------------
// @filename:
// CTranslatorRelcacheToDXL.cpp
//
// @doc:
// Class translating relcache entries into DXL objects
//
// @test:
//
//
//---------------------------------------------------------------------------
#include "postgres.h"
#include "naucrates/md/CMDIdCast.h"
#include "naucrates/md/CMDIdScCmp.h"
#include "naucrates/dxl/gpdb_types.h"
#include "naucrates/md/CMDCastGPDB.h"
#include "naucrates/md/CMDScCmpGPDB.h"
#include "gpopt/translate/CTranslatorUtils.h"
#include "gpopt/translate/CTranslatorRelcacheToDXL.h"
#include "gpopt/translate/CTranslatorScalarToDXL.h"
#include "gpopt/mdcache/CMDAccessor.h"
#include "utils/array.h"
#include "utils/rel.h"
#include "utils/relcache.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
#include "utils/datum.h"
#include "utils/elog.h"
#include "utils/guc.h"
#include "cdb/cdbhash.h"
#include "access/heapam.h"
#include "catalog/pg_exttable.h"
#include "cdb/cdbpartition.h"
#include "cdb/cdbdatalocality.h"
#include "catalog/namespace.h"
#include "catalog/pg_statistic.h"
#define GPDB_COUNT_AGG_OID 2147
#include "gpos/base.h"
#include "gpos/error/CException.h"
#include "naucrates/exception.h"
#include "naucrates/dxl/CDXLUtils.h"
#include "naucrates/dxl/xml/dxltokens.h"
#include "naucrates/md/CMDTypeBoolGPDB.h"
#include "naucrates/md/CMDTypeGenericGPDB.h"
#include "naucrates/md/CMDTypeInt2GPDB.h"
#include "naucrates/md/CMDTypeInt4GPDB.h"
#include "naucrates/md/CMDTypeInt8GPDB.h"
#include "naucrates/md/CMDTypeOidGPDB.h"
#include "naucrates/md/CMDIndexGPDB.h"
#include "naucrates/md/CMDPartConstraintGPDB.h"
#include "naucrates/md/CMDIdRelStats.h"
#include "naucrates/md/CDXLRelStats.h"
#include "naucrates/md/CMDIdColStats.h"
#include "naucrates/md/CDXLColStats.h"
#include "gpopt/base/CUtils.h"
#include "gpopt/gpdbwrappers.h"
using namespace gpdxl;
using namespace gpopt;
static
const ULONG rgulCmpTypeMappings[][2] =
{
{IMDType::EcmptEq, CmptEq},
{IMDType::EcmptNEq, CmptNEq},
{IMDType::EcmptL, CmptLT},
{IMDType::EcmptG, CmptGT},
{IMDType::EcmptGEq, CmptGEq},
{IMDType::EcmptLEq, CmptLEq}
};
// TODO: solimm1 - 01/09/2014: remove function properties map when catalog is fixed to include correct properties
// initialization of function properties map
const CTranslatorRelcacheToDXL::SFuncProps CTranslatorRelcacheToDXL::m_rgfp[] =
{
};
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pimdobj
//
// @doc:
// Retrieve a metadata object from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
IMDCacheObject *
CTranslatorRelcacheToDXL::Pimdobj
(
IMemoryPool *pmp,
CMDAccessor *pmda,
IMDId *pmdid
)
{
IMDCacheObject *pmdcacheobj = NULL;
GPOS_ASSERT(NULL != pmda);
switch(pmdid->Emdidt())
{
case IMDId::EmdidGPDB:
pmdcacheobj = PimdobjGPDB(pmp, pmda, pmdid);
break;
case IMDId::EmdidRelStats:
pmdcacheobj = PimdobjRelStats(pmp, pmdid);
break;
case IMDId::EmdidColStats:
pmdcacheobj = PimdobjColStats(pmp, pmda, pmdid);
break;
case IMDId::EmdidCastFunc:
pmdcacheobj = PimdobjCast(pmp, pmdid);
break;
case IMDId::EmdidScCmp:
pmdcacheobj = PmdobjScCmp(pmp, pmdid);
break;
default:
break;
}
if (NULL == pmdcacheobj)
{
// no match found
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
return pmdcacheobj;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PimdobjGPDB
//
// @doc:
// Retrieve a GPDB metadata object from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
IMDCacheObject *
CTranslatorRelcacheToDXL::PimdobjGPDB
(
IMemoryPool *pmp,
CMDAccessor *pmda,
IMDId *pmdid
)
{
GPOS_ASSERT(pmdid->Emdidt() == CMDIdGPDB::EmdidGPDB);
OID oid = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(0 != oid);
// find out what type of object this oid stands for
if (gpdb::FIndexExists(oid))
{
return Pmdindex(pmp, pmda, pmdid);
}
if (gpdb::FTypeExists(oid))
{
return Pmdtype(pmp, pmdid);
}
if (gpdb::FRelationExists(oid))
{
return Pmdrel(pmp, pmda, pmdid);
}
if (gpdb::FOperatorExists(oid))
{
return Pmdscop(pmp, pmdid);
}
if (gpdb::FAggregateExists(oid))
{
return Pmdagg(pmp, pmdid);
}
if (gpdb::FFunctionExists(oid))
{
return Pmdfunc(pmp, pmdid);
}
if (gpdb::FTriggerExists(oid))
{
return Pmdtrigger(pmp, pmdid);
}
if (gpdb::FCheckConstraintExists(oid))
{
return Pmdcheckconstraint(pmp, pmda, pmdid);
}
// no match found
return NULL;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdnameRel
//
// @doc:
// Return a relation name
//
//---------------------------------------------------------------------------
CMDName *
CTranslatorRelcacheToDXL::PmdnameRel
(
IMemoryPool *pmp,
Relation rel
)
{
GPOS_ASSERT(NULL != rel);
CHAR *szRelName = NameStr(rel->rd_rel->relname);
CWStringDynamic *pstrRelName = CDXLUtils::PstrFromSz(pmp, szRelName);
CMDName *pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrRelName);
GPOS_DELETE(pstrRelName);
return pmdname;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpmdidRelIndexes
//
// @doc:
// Return the indexes defined on the given relation
//
//---------------------------------------------------------------------------
DrgPmdid *
CTranslatorRelcacheToDXL::PdrgpmdidRelIndexes
(
IMemoryPool *pmp,
Relation rel
)
{
GPOS_ASSERT(NULL != rel);
DrgPmdid *pdrgpmdidIndexes = GPOS_NEW(pmp) DrgPmdid(pmp);
List *plIndexOids = NIL;
if (gpdb::FRelPartIsNone(rel->rd_id))
{
// not a partitioned table: obtain indexes directly from the catalog
plIndexOids = gpdb::PlRelationIndexes(rel);
}
else if (gpdb::FRelPartIsRoot(rel->rd_id))
{
// root of partitioned table: aggregate index information across different parts
plIndexOids = PlIndexOidsPartTable(rel);
}
else
{
// interior or leaf partition: do not consider indexes
return pdrgpmdidIndexes;
}
ListCell *plc = NULL;
ForEach (plc, plIndexOids)
{
OID oidIndex = lfirst_oid(plc);
// only add supported indexes
Relation relIndex = gpdb::RelGetRelation(oidIndex);
if (NULL == relIndex)
{
WCHAR wsz[1024];
CWStringStatic str(wsz, 1024);
COstreamString oss(&str);
oss << (ULONG) oidIndex;
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, str.Wsz());
}
GPOS_ASSERT(NULL != relIndex->rd_indextuple);
GPOS_TRY
{
if (FIndexSupported(relIndex))
{
CMDIdGPDB *pmdidIndex = CTranslatorUtils::PmdidWithVersion(pmp, oidIndex);
pdrgpmdidIndexes->Append(pmdidIndex);
}
gpdb::CloseRelation(relIndex);
}
GPOS_CATCH_EX(ex)
{
gpdb::CloseRelation(relIndex);
GPOS_RETHROW(ex);
}
GPOS_CATCH_END;
}
return pdrgpmdidIndexes;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PlIndexOidsPartTable
//
// @doc:
// Return the index oids of a partitioned table
//
//---------------------------------------------------------------------------
List *
CTranslatorRelcacheToDXL::PlIndexOidsPartTable
(
Relation rel
)
{
if (!gpdb::FRelPartIsRoot(rel->rd_id))
{
// not a partitioned table
return NIL;
}
List *plOids = NIL;
LogicalIndexes *plgidx = gpdb::Plgidx(rel->rd_id);
if (NULL == plgidx)
{
return NIL;
}
GPOS_ASSERT(NULL != plgidx);
GPOS_ASSERT(0 <= plgidx->numLogicalIndexes);
const ULONG ulIndexes = (ULONG) plgidx->numLogicalIndexes;
for (ULONG ul = 0; ul < ulIndexes; ul++)
{
LogicalIndexInfo *pidxinfo = (plgidx->logicalIndexInfo)[ul];
plOids = gpdb::PlAppendOid(plOids, pidxinfo->logicalIndexOid);
}
gpdb::GPDBFree(plgidx);
return plOids;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpmdidTriggers
//
// @doc:
// Return the triggers defined on the given relation
//
//---------------------------------------------------------------------------
DrgPmdid *
CTranslatorRelcacheToDXL::PdrgpmdidTriggers
(
IMemoryPool *pmp,
Relation rel
)
{
GPOS_ASSERT(NULL != rel);
if (0 < rel->rd_rel->reltriggers && NULL == rel->trigdesc)
{
gpdb::BuildRelationTriggers(rel);
if (NULL == rel->trigdesc)
{
rel->rd_rel->reltriggers = 0;
}
}
DrgPmdid *pdrgpmdidTriggers = GPOS_NEW(pmp) DrgPmdid(pmp);
const ULONG ulTriggers = rel->rd_rel->reltriggers;
for (ULONG ul = 0; ul < ulTriggers; ul++)
{
Trigger trigger = rel->trigdesc->triggers[ul];
OID oidTrigger = trigger.tgoid;
CMDIdGPDB *pmdidTrigger = CTranslatorUtils::PmdidWithVersion(pmp, oidTrigger);
pdrgpmdidTriggers->Append(pmdidTrigger);
}
return pdrgpmdidTriggers;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpmdidCheckConstraints
//
// @doc:
// Return the check constraints defined on the relation with the given oid
//
//---------------------------------------------------------------------------
DrgPmdid *
CTranslatorRelcacheToDXL::PdrgpmdidCheckConstraints
(
IMemoryPool *pmp,
OID oid
)
{
DrgPmdid *pdrgpmdidCheckConstraints = GPOS_NEW(pmp) DrgPmdid(pmp);
List *plOidCheckConstraints = gpdb::PlCheckConstraint(oid);
ListCell *plcOid = NULL;
ForEach (plcOid, plOidCheckConstraints)
{
OID oidCheckConstraint = lfirst_oid(plcOid);
GPOS_ASSERT(0 != oidCheckConstraint);
CMDIdGPDB *pmdidCheckConstraint = CTranslatorUtils::PmdidWithVersion(pmp, oidCheckConstraint);
pdrgpmdidCheckConstraints->Append(pmdidCheckConstraint);
}
return pdrgpmdidCheckConstraints;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::CheckUnsupportedRelation
//
// @doc:
// Check and fall back to planner for unsupported relations
//
//---------------------------------------------------------------------------
void
CTranslatorRelcacheToDXL::CheckUnsupportedRelation
(
OID oidRel
)
{
if (gpdb::FRelPartIsInterior(oidRel))
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Query on intermediate partition"));
}
List *plPartKeys = gpdb::PlPartitionAttrs(oidRel);
ULONG ulLevels = gpdb::UlListLength(plPartKeys);
if (0 == ulLevels && gpdb::FHasSubclass(oidRel))
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Inherited tables"));
}
if (1 < ulLevels)
{
if (!optimizer_multilevel_partitioning)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Multi-level partitioned tables"));
}
if (!gpdb::FMultilevelPartitionUniform(oidRel))
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Multi-level partitioned tables with non-uniform partitioning structure"));
}
}
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::FTreatAsRandom
//
// @doc:
// Whether we need to treat a hash distributed table as random distributed
//
//---------------------------------------------------------------------------
BOOL
CTranslatorRelcacheToDXL::FTreatAsRandom
(
OID oid,
GpPolicy *pgppolicy
)
{
QueryResource *resource = gpdb::PqrActiveQueryResource();
if (NULL == resource)
{
// no resource has been allocated. In this case we are inside an 'explain',
// no hash to random conversion will happen
return false;
}
List *lRelsType = gpdb::PlActiveRelTypes();
ListCell *lc = NULL;
foreach(lc, lRelsType)
{
CurrentRelType *relType = (CurrentRelType *) lfirst(lc);
if (relType->relid == oid)
{
/* a hash distributed table can be considered as hash if
* 1. The active relation type is hash
* 2. The bucketnum of this relation matches the number of vSegs allocated
*/
if (relType->isHash && pgppolicy->bucketnum == list_length(resource->segments))
{
return false; // keep hash distributed
}
break;
}
}
return true;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdrel
//
// @doc:
// Retrieve a relation from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
IMDRelation *
CTranslatorRelcacheToDXL::Pmdrel
(
IMemoryPool *pmp,
CMDAccessor *pmda,
IMDId *pmdid
)
{
OID oid = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oid);
CheckUnsupportedRelation(oid);
Relation rel = gpdb::RelGetRelation(oid);
if (NULL == rel)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CMDName *pmdname = NULL;
IMDRelation::Erelstoragetype erelstorage = IMDRelation::ErelstorageSentinel;
DrgPmdcol *pdrgpmdcol = NULL;
IMDRelation::Ereldistrpolicy ereldistribution = IMDRelation::EreldistrSentinel;
DrgPul *pdrpulDistrCols = NULL;
DrgPmdid *pdrgpmdidIndexes = NULL;
DrgPmdid *pdrgpmdidTriggers = NULL;
DrgPul *pdrgpulPartKeys = NULL;
ULONG ulLeafPartitions = 0;
BOOL fConvertHashToRandom = false;
DrgPdrgPul *pdrgpdrgpulKeys = NULL;
DrgPmdid *pdrgpmdidCheckConstraints = NULL;
BOOL fTemporary = false;
BOOL fHasOids = false;
IMDRelation *pmdrel = NULL;
GPOS_TRY
{
// get rel name
pmdname = PmdnameRel(pmp, rel);
// get storage type
erelstorage = Erelstorage(rel->rd_rel->relstorage);
// get relation columns
pdrgpmdcol = Pdrgpmdcol(pmp, pmda, rel, erelstorage);
const ULONG ulMaxCols = GPDXL_SYSTEM_COLUMNS + (ULONG) rel->rd_att->natts + 1;
ULONG *pulAttnoMapping = PulAttnoMapping(pmp, pdrgpmdcol, ulMaxCols);
// get distribution policy
GpPolicy *pgppolicy = gpdb::Pdistrpolicy(rel);
ereldistribution = Ereldistribution(pgppolicy);
// determine if table should be treated as randomly distributed, otherwise get distribution columns
if (IMDRelation::EreldistrHash == ereldistribution)
{
fConvertHashToRandom = FTreatAsRandom(oid, pgppolicy);
pdrpulDistrCols = PdrpulDistrCols(pmp, pgppolicy, pdrgpmdcol, ulMaxCols);
}
fConvertHashToRandom = fConvertHashToRandom || gpdb::FChildPartDistributionMismatch(rel);
// collect relation indexes
pdrgpmdidIndexes = PdrgpmdidRelIndexes(pmp, rel);
// collect relation triggers
pdrgpmdidTriggers = PdrgpmdidTriggers(pmp, rel);
// get partition keys
if (IMDRelation::ErelstorageExternal != erelstorage)
{
pdrgpulPartKeys = PdrgpulPartKeys(pmp, rel, oid);
}
BOOL fPartitioned = (NULL != pdrgpulPartKeys && 0 < pdrgpulPartKeys->UlLength());
if (fPartitioned && IMDRelation::ErelstorageAppendOnlyParquet != erelstorage && IMDRelation::ErelstorageExternal != erelstorage)
{
// mark relation as Parquet if one of its children is parquet
if (gpdb::FHasParquetChildren(oid))
{
erelstorage = IMDRelation::ErelstorageAppendOnlyParquet;
}
}
// get number of leaf partitions
if (gpdb::FRelPartIsRoot(oid))
{
ulLeafPartitions = gpdb::UlLeafPartitions(oid);
}
// get key sets
BOOL fAddDefaultKeys = FHasSystemColumns(rel->rd_rel->relkind);
pdrgpdrgpulKeys = PdrgpdrgpulKeys(pmp, oid, fAddDefaultKeys, fPartitioned, pulAttnoMapping);
// collect all check constraints
pdrgpmdidCheckConstraints = PdrgpmdidCheckConstraints(pmp, oid);
fTemporary = rel->rd_istemp;
fHasOids = rel->rd_rel->relhasoids;
GPOS_DELETE_ARRAY(pulAttnoMapping);
gpdb::CloseRelation(rel);
}
GPOS_CATCH_EX(ex)
{
gpdb::CloseRelation(rel);
GPOS_RETHROW(ex);
}
GPOS_CATCH_END;
GPOS_ASSERT(IMDRelation::ErelstorageSentinel != erelstorage);
GPOS_ASSERT(IMDRelation::EreldistrSentinel != ereldistribution);
pmdid->AddRef();
if (IMDRelation::ErelstorageExternal == erelstorage)
{
ExtTableEntry *extentry = gpdb::Pexttable(oid);
// get format error table id
IMDId *pmdidFmtErrTbl = NULL;
if (InvalidOid != extentry->fmterrtbl)
{
pmdidFmtErrTbl = CTranslatorUtils::PmdidWithVersion(pmp, extentry->fmterrtbl);
}
pmdrel = GPOS_NEW(pmp) CMDRelationExternalGPDB
(
pmp,
pmdid,
pmdname,
ereldistribution,
pdrgpmdcol,
pdrpulDistrCols,
fConvertHashToRandom,
pdrgpdrgpulKeys,
pdrgpmdidIndexes,
pdrgpmdidTriggers,
pdrgpmdidCheckConstraints,
extentry->rejectlimit,
('r' == extentry->rejectlimittype),
pmdidFmtErrTbl
);
}
else
{
// get part constraint
CMDPartConstraintGPDB *pmdpartcnstr = PmdpartcnstrRelation(pmp, pmda, oid, pdrgpmdcol);
pmdrel = GPOS_NEW(pmp) CMDRelationGPDB
(
pmp,
pmdid,
pmdname,
fTemporary,
erelstorage,
ereldistribution,
pdrgpmdcol,
pdrpulDistrCols,
pdrgpulPartKeys,
ulLeafPartitions,
fConvertHashToRandom,
pdrgpdrgpulKeys,
pdrgpmdidIndexes,
pdrgpmdidTriggers,
pdrgpmdidCheckConstraints,
pmdpartcnstr,
fHasOids
);
}
return pmdrel;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pdrgpmdcol
//
// @doc:
// Get relation columns
//
//---------------------------------------------------------------------------
DrgPmdcol *
CTranslatorRelcacheToDXL::Pdrgpmdcol
(
IMemoryPool *pmp,
CMDAccessor *pmda,
Relation rel,
IMDRelation::Erelstoragetype erelstorage
)
{
DrgPmdcol *pdrgpmdcol = GPOS_NEW(pmp) DrgPmdcol(pmp);
for (ULONG ul = 0; ul < (ULONG) rel->rd_att->natts; ul++)
{
Form_pg_attribute att = rel->rd_att->attrs[ul];
CMDName *pmdnameCol = CDXLUtils::PmdnameFromSz(pmp, NameStr(att->attname));
// translate the default column value
CDXLNode *pdxlnDefault = NULL;
if (!att->attisdropped)
{
pdxlnDefault = PdxlnDefaultColumnValue(pmp, pmda, rel->rd_att, att->attnum);
}
ULONG ulColLen = ULONG_MAX;
CMDIdGPDB *pmdidCol = CTranslatorUtils::PmdidWithVersion(pmp, att->atttypid);
if ((pmdidCol->FEquals(&CMDIdGPDB::m_mdidBPChar) || pmdidCol->FEquals(&CMDIdGPDB::m_mdidVarChar)) && (VARHDRSZ < att->atttypmod))
{
ulColLen = (ULONG) att->atttypmod - VARHDRSZ;
}
CMDColumn *pmdcol = GPOS_NEW(pmp) CMDColumn
(
pmdnameCol,
att->attnum,
pmdidCol,
!att->attnotnull,
att->attisdropped,
pdxlnDefault /* default value */,
ulColLen
);
pdrgpmdcol->Append(pmdcol);
}
// add system columns
if (FHasSystemColumns(rel->rd_rel->relkind))
{
BOOL fAOTable = IMDRelation::ErelstorageAppendOnlyRows == erelstorage ||
IMDRelation::ErelstorageAppendOnlyParquet == erelstorage;
AddSystemColumns(pmp, pdrgpmdcol, rel->rd_att->tdhasoid, fAOTable);
}
return pdrgpmdcol;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdxlnDefaultColumnValue
//
// @doc:
// Return the dxl representation of column's default value
//
//---------------------------------------------------------------------------
CDXLNode *
CTranslatorRelcacheToDXL::PdxlnDefaultColumnValue
(
IMemoryPool *pmp,
CMDAccessor *pmda,
TupleDesc rd_att,
AttrNumber attno
)
{
GPOS_ASSERT(attno > 0);
Node *pnode = NULL;
// Scan to see if relation has a default for this column
if (NULL != rd_att->constr && 0 < rd_att->constr->num_defval)
{
AttrDefault *defval = rd_att->constr->defval;
INT iNumDef = rd_att->constr->num_defval;
GPOS_ASSERT(NULL != defval);
for (ULONG ulCounter = 0; ulCounter < (ULONG) iNumDef; ulCounter++)
{
if (attno == defval[ulCounter].adnum)
{
// found it, convert string representation to node tree.
pnode = gpdb::Pnode(defval[ulCounter].adbin);
break;
}
}
}
if (NULL == pnode)
{
// get the default value for the type
Form_pg_attribute att_tup = rd_att->attrs[attno - 1];
Oid oidAtttype = att_tup->atttypid;
pnode = gpdb::PnodeTypeDefault(oidAtttype);
}
if (NULL == pnode)
{
return NULL;
}
// translate the default value expression
CTranslatorScalarToDXL sctranslator
(
pmp,
pmda,
NULL, /* pulidgtorCol */
NULL, /* pulidgtorCTE */
0, /* ulQueryLevel */
true, /* m_fQuery */
NULL, /* m_pplstmt */
NULL, /* m_pmappv */
NULL, /* phmulCTEEntries */
NULL /* pdrgpdxlnCTE */
);
return sctranslator.PdxlnScOpFromExpr
(
(Expr *) pnode,
NULL /* pmapvarcolid --- subquery or external variable are not supported in default expression */
);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Ereldistribution
//
// @doc:
// Return the distribution policy of the relation
//
//---------------------------------------------------------------------------
IMDRelation::Ereldistrpolicy
CTranslatorRelcacheToDXL::Ereldistribution
(
GpPolicy *pgppolicy
)
{
if (NULL == pgppolicy)
{
return IMDRelation::EreldistrMasterOnly;
}
if (POLICYTYPE_PARTITIONED == pgppolicy->ptype)
{
if (0 == pgppolicy->nattrs)
{
return IMDRelation::EreldistrRandom;
}
return IMDRelation::EreldistrHash;
}
if (POLICYTYPE_ENTRY == pgppolicy->ptype)
{
return IMDRelation::EreldistrMasterOnly;
}
GPOS_ASSERT(!"Unrecognized distribution policy");
return IMDRelation::EreldistrSentinel;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrpulDistrCols
//
// @doc:
// Get distribution columns
//
//---------------------------------------------------------------------------
DrgPul *
CTranslatorRelcacheToDXL::PdrpulDistrCols
(
IMemoryPool *pmp,
GpPolicy *pgppolicy,
DrgPmdcol *pdrgpmdcol,
ULONG ulSize
)
{
ULONG *pul = GPOS_NEW_ARRAY(pmp , ULONG, ulSize);
for (ULONG ul = 0; ul < pdrgpmdcol->UlLength(); ul++)
{
const IMDColumn *pmdcol = (*pdrgpmdcol)[ul];
INT iAttno = pmdcol->IAttno();
ULONG ulIndex = (ULONG) (GPDXL_SYSTEM_COLUMNS + iAttno);
pul[ulIndex] = ul;
}
DrgPul *pdrpulDistrCols = GPOS_NEW(pmp) DrgPul(pmp);
for (ULONG ul = 0; ul < (ULONG) pgppolicy->nattrs; ul++)
{
AttrNumber attno = pgppolicy->attrs[ul];
pdrpulDistrCols->Append(GPOS_NEW(pmp) ULONG(UlPosition(attno, pul)));
}
GPOS_DELETE_ARRAY(pul);
return pdrpulDistrCols;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::AddSystemColumns
//
// @doc:
// Adding system columns (oid, tid, xmin, etc) in table descriptors
//
//---------------------------------------------------------------------------
void
CTranslatorRelcacheToDXL::AddSystemColumns
(
IMemoryPool *pmp,
DrgPmdcol *pdrgpmdcol,
BOOL fHasOid,
BOOL fAOTable
)
{
for (INT i= SelfItemPointerAttributeNumber; i > FirstLowInvalidHeapAttributeNumber; i--)
{
AttrNumber attno = AttrNumber(i);
GPOS_ASSERT(0 != attno);
if (ObjectIdAttributeNumber == i && !fHasOid)
{
continue;
}
if (FTransactionVisibilityAttribute(i) && fAOTable)
{
// skip transaction attrbutes like xmin, xmax, cmin, cmax for AO tables
continue;
}
// get system name for that attribute
const CWStringConst *pstrSysColName = CTranslatorUtils::PstrSystemColName(attno);
GPOS_ASSERT(NULL != pstrSysColName);
// copy string into column name
CMDName *pmdnameCol = GPOS_NEW(pmp) CMDName(pmp, pstrSysColName);
CMDColumn *pmdcol = GPOS_NEW(pmp) CMDColumn
(
pmdnameCol,
attno,
CTranslatorUtils::PmdidSystemColType(pmp, attno),
false, // fNullable
false, // fDropped
NULL // default value
);
pdrgpmdcol->Append(pmdcol);
}
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::FTransactionVisibilityAttribute
//
// @doc:
// Check if attribute number is one of the system attributes related to
// transaction visibility such as xmin, xmax, cmin, cmax
//
//---------------------------------------------------------------------------
BOOL
CTranslatorRelcacheToDXL::FTransactionVisibilityAttribute
(
INT iAttNo
)
{
return iAttNo == MinTransactionIdAttributeNumber || iAttNo == MaxTransactionIdAttributeNumber ||
iAttNo == MinCommandIdAttributeNumber || iAttNo == MaxCommandIdAttributeNumber;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdindex
//
// @doc:
// Retrieve an index from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
IMDIndex *
CTranslatorRelcacheToDXL::Pmdindex
(
IMemoryPool *pmp,
CMDAccessor *pmda,
IMDId *pmdidIndex
)
{
OID oidIndex = CMDIdGPDB::PmdidConvert(pmdidIndex)->OidObjectId();
GPOS_ASSERT(0 != oidIndex);
Relation relIndex = gpdb::RelGetRelation(oidIndex);
if (NULL == relIndex)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdidIndex->Wsz());
}
const IMDRelation *pmdrel = NULL;
Form_pg_index pgIndex = NULL;
CMDName *pmdname = NULL;
IMDIndex::EmdindexType emdindt = IMDIndex::EmdindSentinel;
IMDId *pmdidItemType = NULL;
GPOS_TRY
{
if (!FIndexSupported(relIndex))
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Index type"));
}
pgIndex = relIndex->rd_index;
GPOS_ASSERT (NULL != pgIndex);
OID oidRel = pgIndex->indrelid;
if (gpdb::FLeafPartition(oidRel))
{
oidRel = gpdb::OidRootPartition(oidRel);
}
CMDIdGPDB *pmdidRel = CTranslatorUtils::PmdidWithVersion(pmp, oidRel);
pmdrel = pmda->Pmdrel(pmdidRel);
if (pmdrel->FPartitioned())
{
LogicalIndexes *plgidx = gpdb::Plgidx(oidRel);
GPOS_ASSERT(NULL != plgidx);
IMDIndex *pmdindex = PmdindexPartTable(pmp, pmda, pmdidIndex, pmdrel, plgidx);
// cleanup
pmdidRel->Release();
gpdb::GPDBFree(plgidx);
gpdb::CloseRelation(relIndex);
return pmdindex;
}
emdindt = IMDIndex::EmdindBtree;
IMDRelation::Erelstoragetype erelstorage = pmdrel->Erelstorage();
if (BITMAP_AM_OID == relIndex->rd_rel->relam || IMDRelation::ErelstorageAppendOnlyRows == erelstorage || IMDRelation::ErelstorageAppendOnlyCols == erelstorage)
{
emdindt = IMDIndex::EmdindBitmap;
pmdidItemType = CTranslatorUtils::PmdidWithVersion(pmp, GPDB_ANY);
}
// get the index name
CHAR *szIndexName = NameStr(relIndex->rd_rel->relname);
CWStringDynamic *pstrName = CDXLUtils::PstrFromSz(pmp, szIndexName);
pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrName);
GPOS_DELETE(pstrName);
pmdidRel->Release();
gpdb::CloseRelation(relIndex);
}
GPOS_CATCH_EX(ex)
{
gpdb::CloseRelation(relIndex);
GPOS_RETHROW(ex);
}
GPOS_CATCH_END;
Relation relTable = gpdb::RelGetRelation(CMDIdGPDB::PmdidConvert(pmdrel->Pmdid())->OidObjectId());
ULONG ulRgSize = GPDXL_SYSTEM_COLUMNS + (ULONG) relTable->rd_att->natts + 1;
gpdb::CloseRelation(relTable); // close relation as early as possible
ULONG *pul = PulAttnoPositionMap(pmp, pmdrel, ulRgSize);
DrgPul *pdrgpulIncludeCols = PdrgpulIndexIncludedColumns(pmp, pmdrel);
// extract the position of the key columns
DrgPul *pdrgpulKeyCols = GPOS_NEW(pmp) DrgPul(pmp);
ULONG ulKeys = pgIndex->indnatts;
for (ULONG ul = 0; ul < ulKeys; ul++)
{
INT iAttno = pgIndex->indkey.values[ul];
GPOS_ASSERT(0 != iAttno && "Index expressions not supported");
pdrgpulKeyCols->Append(GPOS_NEW(pmp) ULONG(UlPosition(iAttno, pul)));
}
pmdidIndex->AddRef();
DrgPmdid *pdrgpmdidOpClasses = PdrgpmdidIndexOpClasses(pmp, pmdidIndex);
CMDIndexGPDB *pmdindex = GPOS_NEW(pmp) CMDIndexGPDB
(
pmp,
pmdidIndex,
pmdname,
CTranslatorUtils::PmdidWithVersion(pmp, pgIndex->indrelid),
pgIndex->indisclustered,
emdindt,
pmdidItemType,
false, // fPartial
pdrgpulKeyCols,
pdrgpulIncludeCols,
pdrgpmdidOpClasses,
NULL // pmdpartcnstr
);
GPOS_DELETE_ARRAY(pul);
return pmdindex;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdindexPartTable
//
// @doc:
// Retrieve an index over a partitioned table from the relcache given its
// mdid
//
//---------------------------------------------------------------------------
IMDIndex *
CTranslatorRelcacheToDXL::PmdindexPartTable
(
IMemoryPool *pmp,
CMDAccessor *pmda,
IMDId *pmdidIndex,
const IMDRelation *pmdrel,
LogicalIndexes *plind
)
{
GPOS_ASSERT(NULL != plind);
GPOS_ASSERT(0 < plind->numLogicalIndexes);
OID oid = CMDIdGPDB::PmdidConvert(pmdidIndex)->OidObjectId();
LogicalIndexInfo *pidxinfo = PidxinfoLookup(plind, oid);
if (NULL == pidxinfo)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdidIndex->Wsz());
}
return PmdindexPartTable(pmp, pmda, pidxinfo, pmdidIndex, pmdrel);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PidxinfoLookup
//
// @doc:
// Lookup an index given its id from the logical indexes structure
//
//---------------------------------------------------------------------------
LogicalIndexInfo *
CTranslatorRelcacheToDXL::PidxinfoLookup
(
LogicalIndexes *plind,
OID oid
)
{
GPOS_ASSERT(NULL != plind && 0 <= plind->numLogicalIndexes);
const ULONG ulIndexes = plind->numLogicalIndexes;
for (ULONG ul = 0; ul < ulIndexes; ul++)
{
LogicalIndexInfo *pidxinfo = (plind->logicalIndexInfo)[ul];
if (oid == pidxinfo->logicalIndexOid)
{
return pidxinfo;
}
}
return NULL;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdindexPartTable
//
// @doc:
// Construct an MD cache index object given its logical index representation
//
//---------------------------------------------------------------------------
IMDIndex *
CTranslatorRelcacheToDXL::PmdindexPartTable
(
IMemoryPool *pmp,
CMDAccessor *pmda,
LogicalIndexInfo *pidxinfo,
IMDId *pmdidIndex,
const IMDRelation *pmdrel
)
{
OID oidIndex = pidxinfo->logicalIndexOid;
Relation relIndex = gpdb::RelGetRelation(oidIndex);
if (NULL == relIndex)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdidIndex->Wsz());
}
if (!FIndexSupported(relIndex))
{
gpdb::CloseRelation(relIndex);
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Index type"));
}
// get the index name
GPOS_ASSERT(NULL != relIndex->rd_index);
Form_pg_index pgIndex = relIndex->rd_index;
CHAR *szIndexName = NameStr(relIndex->rd_rel->relname);
CMDName *pmdname = CDXLUtils::PmdnameFromSz(pmp, szIndexName);
gpdb::CloseRelation(relIndex);
OID oidRel = CMDIdGPDB::PmdidConvert(pmdrel->Pmdid())->OidObjectId();
Relation relTable = gpdb::RelGetRelation(oidRel);
ULONG ulRgSize = GPDXL_SYSTEM_COLUMNS + (ULONG) relTable->rd_att->natts + 1;
gpdb::CloseRelation(relTable);
ULONG *pulAttrMap = PulAttnoPositionMap(pmp, pmdrel, ulRgSize);
DrgPul *pdrgpulIncludeCols = PdrgpulIndexIncludedColumns(pmp, pmdrel);
// extract the position of the key columns
DrgPul *pdrgpulKeyCols = GPOS_NEW(pmp) DrgPul(pmp);
const ULONG ulKeys = pidxinfo->nColumns;
for (ULONG ul = 0; ul < ulKeys; ul++)
{
INT iAttno = pidxinfo->indexKeys[ul];
GPOS_ASSERT(0 != iAttno && "Index expressions not supported");
pdrgpulKeyCols->Append(GPOS_NEW(pmp) ULONG(UlPosition(iAttno, pulAttrMap)));
}
Node *pnodePartCnstr = pidxinfo->partCons;
List *plDefaultLevels = pidxinfo->defaultLevels;
// get number of partitioning levels
List *plPartKeys = gpdb::PlPartitionAttrs(oidRel);
const ULONG ulLevels = gpdb::UlListLength(plPartKeys);
gpdb::FreeList(plPartKeys);
// get relation constraints
List *plDefaultLevelsRel = NIL;
Node *pnodePartCnstrRel = gpdb::PnodePartConstraintRel(oidRel, &plDefaultLevelsRel);
BOOL fUnbounded = (NULL == pnodePartCnstr) && (NIL == plDefaultLevels);
for (ULONG ul = 0; ul < ulLevels; ul++)
{
fUnbounded = fUnbounded && FDefaultPartition(plDefaultLevelsRel, ul);
}
DrgPul *pdrgpulDefaultLevels = GPOS_NEW(pmp) DrgPul(pmp);
for (ULONG ul = 0; ul < ulLevels; ul++)
{
if (fUnbounded || FDefaultPartition(plDefaultLevels, ul))
{
pdrgpulDefaultLevels->Append(GPOS_NEW(pmp) ULONG(ul));
}
}
BOOL fPartial = (NULL != pnodePartCnstr || NIL != plDefaultLevels);
if (NULL == pnodePartCnstr)
{
if (NIL == plDefaultLevels)
{
// NULL part constraints means all non-default partitions -> get constraint from the part table
pnodePartCnstr = pnodePartCnstrRel;
}
else
{
pnodePartCnstr = gpdb::PnodeMakeBoolConst(false /*value*/, false /*isull*/);
}
}
CMDPartConstraintGPDB *pmdpartcnstr = PmdpartcnstrIndex(pmp, pmda, pmdrel, pnodePartCnstr, pdrgpulDefaultLevels, fUnbounded);
pdrgpulDefaultLevels->Release();
pmdrel->Pmdid()->AddRef();
pmdidIndex->AddRef();
GPOS_ASSERT(INDTYPE_BITMAP == pidxinfo->indType || INDTYPE_BTREE == pidxinfo->indType);
IMDIndex::EmdindexType emdindt = IMDIndex::EmdindBtree;
IMDId *pmdidItemType = NULL;
if (INDTYPE_BITMAP == pidxinfo->indType)
{
emdindt = IMDIndex::EmdindBitmap;
pmdidItemType = CTranslatorUtils::PmdidWithVersion(pmp, GPDB_ANY);
}
DrgPmdid *pdrgpmdidOpClasses = PdrgpmdidIndexOpClasses(pmp, pmdidIndex);
CMDIndexGPDB *pmdindex = GPOS_NEW(pmp) CMDIndexGPDB
(
pmp,
pmdidIndex,
pmdname,
pmdrel->Pmdid(),
pgIndex->indisclustered,
emdindt,
pmdidItemType,
fPartial,
pdrgpulKeyCols,
pdrgpulIncludeCols,
pdrgpmdidOpClasses,
pmdpartcnstr
);
GPOS_DELETE_ARRAY(pulAttrMap);
return pmdindex;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::FDefaultPartition
//
// @doc:
// Check whether the default partition at level one is included
//
//---------------------------------------------------------------------------
BOOL
CTranslatorRelcacheToDXL::FDefaultPartition
(
List *plDefaultLevels,
ULONG ulLevel
)
{
if (NIL == plDefaultLevels)
{
return false;
}
ListCell *plc = NULL;
ForEach (plc, plDefaultLevels)
{
ULONG ulDefaultLevel = (ULONG) lfirst_int(plc);
if (ulLevel == ulDefaultLevel)
{
return true;
}
}
return false;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpulIndexIncludedColumns
//
// @doc:
// Compute the included colunms in an index
//
//---------------------------------------------------------------------------
DrgPul *
CTranslatorRelcacheToDXL::PdrgpulIndexIncludedColumns
(
IMemoryPool *pmp,
const IMDRelation *pmdrel
)
{
// TODO: raghav, 3/19/2012; currently we assume that all the columns
// in the table are available from the index.
DrgPul *pdrgpulIncludeCols = GPOS_NEW(pmp) DrgPul(pmp);
const ULONG ulIncludedCols = pmdrel->UlColumns();
for (ULONG ul = 0; ul < ulIncludedCols; ul++)
{
if (!pmdrel->Pmdcol(ul)->FDropped())
{
pdrgpulIncludeCols->Append(GPOS_NEW(pmp) ULONG(ul));
}
}
return pdrgpulIncludeCols;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::UlPosition
//
// @doc:
// Return the position of a given attribute
//
//---------------------------------------------------------------------------
ULONG
CTranslatorRelcacheToDXL::UlPosition
(
INT iAttno,
ULONG *pul
)
{
ULONG ulIndex = (ULONG) (GPDXL_SYSTEM_COLUMNS + iAttno);
ULONG ulPos = pul[ulIndex];
GPOS_ASSERT(ULONG_MAX != ulPos);
return ulPos;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PulAttnoPositionMap
//
// @doc:
// Populate the attribute to position mapping
//
//---------------------------------------------------------------------------
ULONG *
CTranslatorRelcacheToDXL::PulAttnoPositionMap
(
IMemoryPool *pmp,
const IMDRelation *pmdrel,
ULONG ulSize
)
{
GPOS_ASSERT(NULL != pmdrel);
const ULONG ulIncludedCols = pmdrel->UlColumns();
GPOS_ASSERT(ulIncludedCols <= ulSize);
ULONG *pul = GPOS_NEW_ARRAY(pmp , ULONG, ulSize);
for (ULONG ul = 0; ul < ulSize; ul++)
{
pul[ul] = ULONG_MAX;
}
for (ULONG ul = 0; ul < ulIncludedCols; ul++)
{
const IMDColumn *pmdcol = pmdrel->Pmdcol(ul);
INT iAttno = pmdcol->IAttno();
ULONG ulIndex = (ULONG) (GPDXL_SYSTEM_COLUMNS + iAttno);
GPOS_ASSERT(ulSize > ulIndex);
pul[ulIndex] = ul;
}
return pul;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdtype
//
// @doc:
// Retrieve a type from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
IMDType *
CTranslatorRelcacheToDXL::Pmdtype
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
OID oidType = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidType);
// check for supported base types
switch (oidType)
{
case GPDB_INT2_OID:
return GPOS_NEW(pmp) CMDTypeInt2GPDB(pmp);
case GPDB_INT4_OID:
return GPOS_NEW(pmp) CMDTypeInt4GPDB(pmp);
case GPDB_INT8_OID:
return GPOS_NEW(pmp) CMDTypeInt8GPDB(pmp);
case GPDB_BOOL:
return GPOS_NEW(pmp) CMDTypeBoolGPDB(pmp);
case GPDB_OID_OID:
return GPOS_NEW(pmp) CMDTypeOidGPDB(pmp);
}
// continue to construct a generic type
INT iFlags = TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
TYPECACHE_CMP_PROC | TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO | TYPECACHE_TUPDESC;
TypeCacheEntry *ptce = gpdb::PtceLookup(oidType, iFlags);
// get type name
CMDName *pmdname = PmdnameType(pmp, pmdid);
BOOL fFixedLength = false;
ULONG ulLength = 0;
if (0 < ptce->typlen)
{
fFixedLength = true;
ulLength = ptce->typlen;
}
BOOL fByValue = ptce->typbyval;
// collect ids of different comparison operators for types
CMDIdGPDB *pmdidOpEq = CTranslatorUtils::PmdidWithVersion(pmp, ptce->eq_opr);
CMDIdGPDB *pmdidOpNEq = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidInverseOp(ptce->eq_opr));
CMDIdGPDB *pmdidOpLT = CTranslatorUtils::PmdidWithVersion(pmp, ptce->lt_opr);
CMDIdGPDB *pmdidOpLEq = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidInverseOp(ptce->gt_opr));
CMDIdGPDB *pmdidOpGT = CTranslatorUtils::PmdidWithVersion(pmp, ptce->gt_opr);
CMDIdGPDB *pmdidOpGEq = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidInverseOp(ptce->lt_opr));
CMDIdGPDB *pmdidOpComp = CTranslatorUtils::PmdidWithVersion(pmp, ptce->cmp_proc);
BOOL fHashable = gpdb::FOpHashJoinable(ptce->eq_opr);
BOOL fComposite = gpdb::FCompositeType(oidType);
// get standard aggregates
CMDIdGPDB *pmdidMin = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidAggregate("min", oidType));
CMDIdGPDB *pmdidMax = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidAggregate("max", oidType));
CMDIdGPDB *pmdidAvg = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidAggregate("avg", oidType));
CMDIdGPDB *pmdidSum = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidAggregate("sum", oidType));
// count aggregate is the same for all types
CMDIdGPDB *pmdidCount = CTranslatorUtils::PmdidWithVersion(pmp, GPDB_COUNT_AGG_OID);
// check if type is composite
CMDIdGPDB *pmdidTypeRelid = NULL;
if (fComposite)
{
pmdidTypeRelid = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidTypeRelid(oidType));
}
// get array type mdid
CMDIdGPDB *pmdidTypeArray = CTranslatorUtils::PmdidWithVersion(pmp, gpdb::OidArrayType(oidType));
BOOL fRedistributable = gpdb::FGreenplumDbHashable(oidType);
pmdid->AddRef();
return GPOS_NEW(pmp) CMDTypeGenericGPDB
(
pmp,
pmdid,
pmdname,
fRedistributable,
fFixedLength,
ulLength,
fByValue,
pmdidOpEq,
pmdidOpNEq,
pmdidOpLT,
pmdidOpLEq,
pmdidOpGT,
pmdidOpGEq,
pmdidOpComp,
pmdidMin,
pmdidMax,
pmdidAvg,
pmdidSum,
pmdidCount,
fHashable,
fComposite,
pmdidTypeRelid,
pmdidTypeArray,
ptce->typlen
);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdscop
//
// @doc:
// Retrieve a scalar operator from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
CMDScalarOpGPDB *
CTranslatorRelcacheToDXL::Pmdscop
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
OID oidOp = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidOp);
// get operator name
CHAR *szName = gpdb::SzOpName(oidOp);
if (NULL == szName)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CMDName *pmdname = CDXLUtils::PmdnameFromSz(pmp, szName);
OID oidLeft = InvalidOid;
OID oidRight = InvalidOid;
// get operator argument types
gpdb::GetOpInputTypes(oidOp, &oidLeft, &oidRight);
CMDIdGPDB *pmdidTypeLeft = NULL;
CMDIdGPDB *pmdidTypeRight = NULL;
if (InvalidOid != oidLeft)
{
pmdidTypeLeft = CTranslatorUtils::PmdidWithVersion(pmp, oidLeft);
}
if (InvalidOid != oidRight)
{
pmdidTypeRight = CTranslatorUtils::PmdidWithVersion(pmp, oidRight);
}
// get comparison type
CmpType cmpt = (CmpType) gpdb::UlCmpt(oidOp, oidLeft, oidRight);
IMDType::ECmpType ecmpt = Ecmpt(cmpt);
// get func oid
OID oidFunc = gpdb::OidOpFunc(oidOp);
GPOS_ASSERT(InvalidOid != oidFunc);
CMDIdGPDB *pmdidFunc = CTranslatorUtils::PmdidWithVersion(pmp, oidFunc);
// get result type
OID oidResult = gpdb::OidFuncRetType(oidFunc);
GPOS_ASSERT(InvalidOid != oidResult);
CMDIdGPDB *pmdidTypeResult = CTranslatorUtils::PmdidWithVersion(pmp, oidResult);
// get commutator and inverse
CMDIdGPDB *pmdidOpCommute = NULL;
OID oidCommute = gpdb::OidCommutatorOp(oidOp);
if(InvalidOid != oidCommute)
{
pmdidOpCommute = CTranslatorUtils::PmdidWithVersion(pmp, oidCommute);
}
CMDIdGPDB *pmdidOpInverse = NULL;
OID oidInverse = gpdb::OidInverseOp(oidOp);
if(InvalidOid != oidInverse)
{
pmdidOpInverse = CTranslatorUtils::PmdidWithVersion(pmp, oidInverse);
}
BOOL fReturnsNullOnNullInput = gpdb::FOpStrict(oidOp);
pmdid->AddRef();
CMDScalarOpGPDB *pmdscop = GPOS_NEW(pmp) CMDScalarOpGPDB
(
pmp,
pmdid,
pmdname,
pmdidTypeLeft,
pmdidTypeRight,
pmdidTypeResult,
pmdidFunc,
pmdidOpCommute,
pmdidOpInverse,
ecmpt,
fReturnsNullOnNullInput,
PdrgpmdidScOpOpClasses(pmp, pmdid)
);
return pmdscop;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::LookupFuncProps
//
// @doc:
// Lookup function properties
//
//---------------------------------------------------------------------------
void
CTranslatorRelcacheToDXL::LookupFuncProps
(
OID oidFunc,
IMDFunction::EFuncStbl *pefs, // output: function stability
IMDFunction::EFuncDataAcc *pefda, // output: function datya access
BOOL *fStrict, // output: is function strict?
BOOL *fReturnsSet // output: does function return set?
)
{
GPOS_ASSERT(NULL != pefs);
GPOS_ASSERT(NULL != pefda);
GPOS_ASSERT(NULL != fStrict);
GPOS_ASSERT(NULL != fReturnsSet);
// lookup function properties first n the local map
const ULONG ulSize = GPOS_ARRAY_SIZE(m_rgfp);
for (ULONG ul = 0; ul < ulSize; ul++)
{
if (m_rgfp[ul].Oid() == oidFunc)
{
*pefs = m_rgfp[ul].Efs();
*pefda = m_rgfp[ul].Efda();
*fStrict = m_rgfp[ul].FStrict();
*fReturnsSet = m_rgfp[ul].FReturnsSet();
return;
}
}
CHAR cFuncStability = gpdb::CFuncStability(oidFunc);
*pefs = EFuncStability(cFuncStability);
CHAR cFuncDataAccess = gpdb::CFuncDataAccess(oidFunc);
*pefda = EFuncDataAccess(cFuncDataAccess);
*fReturnsSet = gpdb::FFuncRetset(oidFunc);
*fStrict = gpdb::FFuncStrict(oidFunc);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdfunc
//
// @doc:
// Retrieve a function from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
CMDFunctionGPDB *
CTranslatorRelcacheToDXL::Pmdfunc
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
OID oidFunc = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidFunc);
// get func name
CHAR *szName = gpdb::SzFuncName(oidFunc);
if (NULL == szName)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CWStringDynamic *pstrFuncName = CDXLUtils::PstrFromSz(pmp, szName);
CMDName *pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrFuncName);
// CMDName ctor created a copy of the string
GPOS_DELETE(pstrFuncName);
// get result type
OID oidResult = gpdb::OidFuncRetType(oidFunc);
GPOS_ASSERT(InvalidOid != oidResult);
CMDIdGPDB *pmdidTypeResult = CTranslatorUtils::PmdidWithVersion(pmp, oidResult);
// get output argument types if any
List *plOutArgTypes = gpdb::PlFuncOutputArgTypes(oidFunc);
DrgPmdid *pdrgpmdidArgTypes = NULL;
if (NULL != plOutArgTypes)
{
ListCell *plc = NULL;
pdrgpmdidArgTypes = GPOS_NEW(pmp) DrgPmdid(pmp);
ForEach (plc, plOutArgTypes)
{
OID oidArgType = lfirst_oid(plc);
GPOS_ASSERT(InvalidOid != oidArgType);
CMDIdGPDB *pmdidArgType = CTranslatorUtils::PmdidWithVersion(pmp, oidArgType);
pdrgpmdidArgTypes->Append(pmdidArgType);
}
gpdb::GPDBFree(plOutArgTypes);
}
IMDFunction::EFuncStbl efs = IMDFunction::EfsImmutable;
IMDFunction::EFuncDataAcc efda = IMDFunction::EfdaNoSQL;
BOOL fStrict = true;
BOOL fReturnsSet = true;
LookupFuncProps(oidFunc, &efs, &efda, &fStrict, &fReturnsSet);
pmdid->AddRef();
CMDFunctionGPDB *pmdfunc = GPOS_NEW(pmp) CMDFunctionGPDB
(
pmp,
pmdid,
pmdname,
pmdidTypeResult,
pdrgpmdidArgTypes,
fReturnsSet,
efs,
efda,
fStrict
);
return pmdfunc;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdagg
//
// @doc:
// Retrieve an aggregate from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
CMDAggregateGPDB *
CTranslatorRelcacheToDXL::Pmdagg
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
OID oidAgg = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidAgg);
// get agg name
CHAR *szName = gpdb::SzFuncName(oidAgg);
if (NULL == szName)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CWStringDynamic *pstrAggName = CDXLUtils::PstrFromSz(pmp, szName);
CMDName *pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrAggName);
// CMDName ctor created a copy of the string
GPOS_DELETE(pstrAggName);
// get result type
OID oidResult = gpdb::OidFuncRetType(oidAgg);
GPOS_ASSERT(InvalidOid != oidResult);
CMDIdGPDB *pmdidTypeResult = CTranslatorUtils::PmdidWithVersion(pmp, oidResult);
IMDId *pmdidTypeIntermediate = PmdidAggIntermediateResultType(pmp, pmdid);
pmdid->AddRef();
BOOL fOrdered = gpdb::FOrderedAgg(oidAgg);
// GPDB does not support splitting of ordered aggs and aggs without a
// preliminary function
BOOL fSplittable = !fOrdered && gpdb::FAggHasPrelimFunc(oidAgg);
// cannot use hash agg for ordered aggs or aggs without a prelim func
// due to the fact that hashAgg may spill
BOOL fHashAggCapable = !fOrdered && gpdb::FAggHasPrelimFunc(oidAgg);
CMDAggregateGPDB *pmdagg = GPOS_NEW(pmp) CMDAggregateGPDB
(
pmp,
pmdid,
pmdname,
pmdidTypeResult,
pmdidTypeIntermediate,
fOrdered,
fSplittable,
fHashAggCapable
);
return pmdagg;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdtrigger
//
// @doc:
// Retrieve a trigger from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
CMDTriggerGPDB *
CTranslatorRelcacheToDXL::Pmdtrigger
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
OID oidTrigger = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidTrigger);
// get trigger name
CHAR *szName = gpdb::SzTriggerName(oidTrigger);
if (NULL == szName)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CWStringDynamic *pstrTriggerName = CDXLUtils::PstrFromSz(pmp, szName);
CMDName *pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrTriggerName);
GPOS_DELETE(pstrTriggerName);
// get relation oid
OID oidRel = gpdb::OidTriggerRelid(oidTrigger);
GPOS_ASSERT(InvalidOid != oidRel);
CMDIdGPDB *pmdidRel = CTranslatorUtils::PmdidWithVersion(pmp, oidRel);
// get function oid
OID oidFunc = gpdb::OidTriggerFuncid(oidTrigger);
GPOS_ASSERT(InvalidOid != oidFunc);
CMDIdGPDB *pmdidFunc = CTranslatorUtils::PmdidWithVersion(pmp, oidFunc);
// get type
INT iType = gpdb::ITriggerType(oidTrigger);
// is trigger enabled
BOOL fEnabled = gpdb::FTriggerEnabled(oidTrigger);
pmdid->AddRef();
CMDTriggerGPDB *pmdtrigger = GPOS_NEW(pmp) CMDTriggerGPDB
(
pmp,
pmdid,
pmdname,
pmdidRel,
pmdidFunc,
iType,
fEnabled
);
return pmdtrigger;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pmdcheckconstraint
//
// @doc:
// Retrieve a check constraint from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
CMDCheckConstraintGPDB *
CTranslatorRelcacheToDXL::Pmdcheckconstraint
(
IMemoryPool *pmp,
CMDAccessor *pmda,
IMDId *pmdid
)
{
OID oidCheckConstraint = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidCheckConstraint);
// get name of the check constraint
CHAR *szName = gpdb::SzCheckConstraintName(oidCheckConstraint);
if (NULL == szName)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CWStringDynamic *pstrCheckConstraintName = CDXLUtils::PstrFromSz(pmp, szName);
CMDName *pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrCheckConstraintName);
GPOS_DELETE(pstrCheckConstraintName);
// get relation oid associated with the check constraint
OID oidRel = gpdb::OidCheckConstraintRelid(oidCheckConstraint);
GPOS_ASSERT(InvalidOid != oidRel);
CMDIdGPDB *pmdidRel = CTranslatorUtils::PmdidWithVersion(pmp, oidRel);
// translate the check constraint expression
Node *pnode = gpdb::PnodeCheckConstraint(oidCheckConstraint);
GPOS_ASSERT(NULL != pnode);
CTranslatorScalarToDXL sctranslator
(
pmp,
pmda,
NULL, /* pulidgtorCol */
NULL, /* pulidgtorCTE */
0, /* ulQueryLevel */
true, /* m_fQuery */
NULL, /* m_pplstmt */
NULL, /* m_pmappv */
NULL, /* phmulCTEEntries */
NULL /* pdrgpdxlnCTE */
);
// generate a mock mapping between var to column information
CMappingVarColId *pmapvarcolid = GPOS_NEW(pmp) CMappingVarColId(pmp);
DrgPdxlcd *pdrgpdxlcd = GPOS_NEW(pmp) DrgPdxlcd(pmp);
const IMDRelation *pmdrel = pmda->Pmdrel(pmdidRel);
const ULONG ulLen = pmdrel->UlColumns();
for (ULONG ul = 0; ul < ulLen; ul++)
{
const IMDColumn *pmdcol = pmdrel->Pmdcol(ul);
CMDName *pmdnameCol = GPOS_NEW(pmp) CMDName(pmp, pmdcol->Mdname().Pstr());
CMDIdGPDB *pmdidColType = CMDIdGPDB::PmdidConvert(pmdcol->PmdidType());
pmdidColType->AddRef();
// create a column descriptor for the column
CDXLColDescr *pdxlcd = GPOS_NEW(pmp) CDXLColDescr
(
pmp,
pmdnameCol,
ul + 1 /*ulColId*/,
pmdcol->IAttno(),
pmdidColType,
false /* fColDropped */
);
pdrgpdxlcd->Append(pdxlcd);
}
pmapvarcolid->LoadColumns(0 /*ulQueryLevel */, 1 /* rteIndex */, pdrgpdxlcd);
// translate the check constraint expression
CDXLNode *pdxlnScalar = sctranslator.PdxlnScOpFromExpr((Expr *) pnode, pmapvarcolid);
// cleanup
pdrgpdxlcd->Release();
GPOS_DELETE(pmapvarcolid);
pmdid->AddRef();
return GPOS_NEW(pmp) CMDCheckConstraintGPDB
(
pmp,
pmdid,
pmdname,
pmdidRel,
pdxlnScalar
);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdnameType
//
// @doc:
// Retrieve a type's name from the relcache given its metadata id.
//
//---------------------------------------------------------------------------
CMDName *
CTranslatorRelcacheToDXL::PmdnameType
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
OID oidType = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidType);
CHAR *szTypeName = gpdb::SzTypeName(oidType);
GPOS_ASSERT(NULL != szTypeName);
CWStringDynamic *pstrName = CDXLUtils::PstrFromSz(pmp, szTypeName);
CMDName *pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrName);
// cleanup
GPOS_DELETE(pstrName);
return pmdname;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::EFuncStability
//
// @doc:
// Get function stability property from the GPDB character representation
//
//---------------------------------------------------------------------------
CMDFunctionGPDB::EFuncStbl
CTranslatorRelcacheToDXL::EFuncStability
(
CHAR c
)
{
CMDFunctionGPDB::EFuncStbl efuncstbl = CMDFunctionGPDB::EfsSentinel;
switch (c)
{
case 's':
efuncstbl = CMDFunctionGPDB::EfsStable;
break;
case 'i':
efuncstbl = CMDFunctionGPDB::EfsImmutable;
break;
case 'v':
efuncstbl = CMDFunctionGPDB::EfsVolatile;
break;
default:
GPOS_ASSERT(!"Invalid stability property");
}
return efuncstbl;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::EFuncDataAccess
//
// @doc:
// Get function data access property from the GPDB character representation
//
//---------------------------------------------------------------------------
CMDFunctionGPDB::EFuncDataAcc
CTranslatorRelcacheToDXL::EFuncDataAccess
(
CHAR c
)
{
CMDFunctionGPDB::EFuncDataAcc efda = CMDFunctionGPDB::EfdaSentinel;
switch (c)
{
case 'n':
efda = CMDFunctionGPDB::EfdaNoSQL;
break;
case 'c':
efda = CMDFunctionGPDB::EfdaContainsSQL;
break;
case 'r':
efda = CMDFunctionGPDB::EfdaReadsSQLData;
break;
case 'm':
efda = CMDFunctionGPDB::EfdaModifiesSQLData;
break;
default:
GPOS_ASSERT(!"Invalid data access property");
}
return efda;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdidAggIntermediateResultType
//
// @doc:
// Retrieve the type id of an aggregate's intermediate results
//
//---------------------------------------------------------------------------
IMDId *
CTranslatorRelcacheToDXL::PmdidAggIntermediateResultType
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
OID oidAgg = CMDIdGPDB::PmdidConvert(pmdid)->OidObjectId();
GPOS_ASSERT(InvalidOid != oidAgg);
OID oidTypeIntermediateResult = gpdb::OidAggIntermediateResultType(oidAgg);
return CTranslatorUtils::PmdidWithVersion(pmp, oidTypeIntermediateResult);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdidFunc
//
// @doc:
// Return the id of the function with the given name
//
//---------------------------------------------------------------------------
IMDId *
CTranslatorRelcacheToDXL::PmdidFunc
(
IMemoryPool *pmp,
const WCHAR *wszFuncName
)
{
CHAR *szFuncName = CDXLUtils::SzFromWsz(pmp, wszFuncName);
FuncCandidateList fcl = gpdb::FclFuncCandidates
(
ListMake1(gpdb::PvalMakeString(szFuncName)),
-1 /* nargs */
);
if (NULL == fcl)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, wszFuncName);
}
GPOS_DELETE_ARRAY(szFuncName);
return CTranslatorUtils::PmdidWithVersion(pmp, fcl->oid);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PimdobjRelStats
//
// @doc:
// Retrieve relation statistics from relcache
//
//---------------------------------------------------------------------------
IMDCacheObject *
CTranslatorRelcacheToDXL::PimdobjRelStats
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
CMDIdRelStats *pmdidRelStats = CMDIdRelStats::PmdidConvert(pmdid);
IMDId *pmdidRel = pmdidRelStats->PmdidRel();
OID oidRelation = CMDIdGPDB::PmdidConvert(pmdidRel)->OidObjectId();
Relation rel = gpdb::RelGetRelation(oidRelation);
if (NULL == rel)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
double rows = 0.0;
CMDName *pmdname = NULL;
GPOS_TRY
{
// get rel name
CHAR *szRelName = NameStr(rel->rd_rel->relname);
CWStringDynamic *pstrRelName = CDXLUtils::PstrFromSz(pmp, szRelName);
pmdname = GPOS_NEW(pmp) CMDName(pmp, pstrRelName);
// CMDName ctor created a copy of the string
GPOS_DELETE(pstrRelName);
BlockNumber pages = 0;
GpPolicy *pgppolicy = gpdb::Pdistrpolicy(rel);
if (!pgppolicy ||pgppolicy->ptype != POLICYTYPE_PARTITIONED)
{
gpdb::EstimateRelationSize(rel, NULL, &pages, &rows);
}
else
{
rows = rel->rd_rel->reltuples;
}
pmdidRelStats->AddRef();
gpdb::CloseRelation(rel);
}
GPOS_CATCH_EX(ex)
{
gpdb::CloseRelation(rel);
GPOS_RETHROW(ex);
}
GPOS_CATCH_END;
BOOL fEmptyStats = false;
if (rows == 0.0)
{
fEmptyStats = true;
}
CDXLRelStats *pdxlrelstats = GPOS_NEW(pmp) CDXLRelStats
(
pmp,
pmdidRelStats,
pmdname,
CDouble(rows),
fEmptyStats
);
return pdxlrelstats;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PimdobjColStats
//
// @doc:
// Retrieve column statistics from relcache
//
//---------------------------------------------------------------------------
IMDCacheObject *
CTranslatorRelcacheToDXL::PimdobjColStats
(
IMemoryPool *pmp,
CMDAccessor *pmda,
IMDId *pmdid
)
{
CMDIdColStats *pmdidColStats = CMDIdColStats::PmdidConvert(pmdid);
IMDId *pmdidRel = pmdidColStats->PmdidRel();
ULONG ulPos = pmdidColStats->UlPos();
OID oidRelation = CMDIdGPDB::PmdidConvert(pmdidRel)->OidObjectId();
Relation rel = gpdb::RelGetRelation(oidRelation);
if (NULL == rel)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
const IMDRelation *pmdrel = pmda->Pmdrel(pmdidRel);
const IMDColumn *pmdcol = pmdrel->Pmdcol(ulPos);
AttrNumber attrnum = (AttrNumber) pmdcol->IAttno();
// number of rows from pg_class
CDouble dRows(rel->rd_rel->reltuples);
// extract column name and type
CMDName *pmdnameCol = GPOS_NEW(pmp) CMDName(pmp, pmdcol->Mdname().Pstr());
OID oidAttType = CMDIdGPDB::PmdidConvert(pmdcol->PmdidType())->OidObjectId();
gpdb::CloseRelation(rel);
DrgPdxlbucket *pdrgpdxlbucket = GPOS_NEW(pmp) DrgPdxlbucket(pmp);
if (0 > attrnum)
{
pmdidColStats->AddRef();
return PdxlcolstatsSystemColumn
(
pmp,
oidRelation,
pmdidColStats,
pmdnameCol,
oidAttType,
attrnum,
pdrgpdxlbucket,
dRows
);
}
// extract out histogram and mcv information from pg_statistic
HeapTuple heaptupleStats = gpdb::HtAttrStats(oidRelation, attrnum);
// if there is no colstats
if (!HeapTupleIsValid(heaptupleStats))
{
pdrgpdxlbucket->Release();
pmdidColStats->AddRef();
CDouble dWidth = CStatistics::DDefaultColumnWidth;
if (!pmdcol->FDropped())
{
CMDIdGPDB *pmdidAttType = GPOS_NEW(pmp) CMDIdGPDB(oidAttType);
IMDType *pmdtype = Pmdtype(pmp, pmdidAttType);
dWidth = CStatisticsUtils::DDefaultColumnWidth(pmdtype);
pmdtype->Release();
pmdidAttType->Release();
}
return CDXLColStats::PdxlcolstatsDummy(pmp, pmdidColStats, pmdnameCol, dWidth);
}
Datum *pdrgdatumMCVValues = NULL;
int iNumMCVValues = 0;
float4 *pdrgfMCVFrequencies = NULL;
int iNumMCVFrequencies = 0;
Datum *pdrgdatumHistValues = NULL;
int iNumHistValues = 0;
(void) gpdb::FGetAttrStatsSlot
(
heaptupleStats,
oidAttType,
-1,
STATISTIC_KIND_MCV,
InvalidOid,
&pdrgdatumMCVValues, &iNumMCVValues,
&pdrgfMCVFrequencies, &iNumMCVFrequencies
);
if (iNumMCVValues != iNumMCVFrequencies)
{
// if the number of MCVs and number of MCFs do not match, we discard the MCVs and MCFs
gpdb::FreeAttrStatsSlot(oidAttType, pdrgdatumMCVValues, iNumMCVValues, pdrgfMCVFrequencies, iNumMCVFrequencies);
iNumMCVValues = 0;
iNumMCVFrequencies = 0;
pdrgdatumMCVValues = NULL;
pdrgfMCVFrequencies = NULL;
elog(LOG, "The number of most common values and frequencies do not match on column %ls of table %ls.",
pmdcol->Mdname().Pstr()->Wsz(), pmdrel->Mdname().Pstr()->Wsz());
}
Form_pg_statistic fpsStats = (Form_pg_statistic) GETSTRUCT(heaptupleStats);
// null frequency and NDV
CDouble dNullFrequency(0.0);
int iNullNDV = 0;
if (CStatistics::DEpsilon < fpsStats->stanullfrac)
{
dNullFrequency = fpsStats->stanullfrac;
iNullNDV = 1;
}
// fix mcv and null frequencies (sometimes they can add up to more than 1.0)
NormalizeFrequencies(pdrgfMCVFrequencies, (ULONG) iNumMCVValues, &dNullFrequency);
// column width
CDouble dWidth = CDouble(fpsStats->stawidth);
// calculate total number of distinct values
CDouble dDistinct(1.0);
if (fpsStats->stadistinct < 0)
{
GPOS_ASSERT(fpsStats->stadistinct > -1.01);
dDistinct = dRows * CDouble(-fpsStats->stadistinct);
}
else
{
dDistinct = CDouble(fpsStats->stadistinct);
}
dDistinct = dDistinct.FpCeil();
// total MCV frequency
CDouble dMCFSum = 0.0;
for (ULONG ul = 0; ul < iNumMCVValues; ul++)
{
dMCFSum = dMCFSum + CDouble(pdrgfMCVFrequencies[ul]);
}
// get histogram datums from pg_statistic entry
(void) gpdb::FGetAttrStatsSlot
(
heaptupleStats,
oidAttType,
-1,
STATISTIC_KIND_HISTOGRAM,
InvalidOid,
&pdrgdatumHistValues, &iNumHistValues,
NULL, NULL);
// transform all the bits and pieces from pg_statistic
// to a single bucket structure
DrgPdxlbucket *pdrgpdxlbucketTransformed =
PdrgpdxlbucketTransformStats
(
pmp,
oidAttType,
dDistinct,
dNullFrequency,
pdrgdatumMCVValues,
pdrgfMCVFrequencies,
ULONG(iNumMCVValues),
pdrgdatumHistValues,
ULONG(iNumHistValues)
);
GPOS_ASSERT(NULL != pdrgpdxlbucketTransformed);
CDouble dNDVBuckets(0.0);
CDouble dFreqBuckets(0.0);
const ULONG ulBuckets = pdrgpdxlbucketTransformed->UlLength();
for (ULONG ul = 0; ul < ulBuckets; ul++)
{
CDXLBucket *pdxlbucket = (*pdrgpdxlbucketTransformed)[ul];
dNDVBuckets = dNDVBuckets + pdxlbucket->DDistinct();
dFreqBuckets = dFreqBuckets + pdxlbucket->DFrequency();
}
// there will be remaining tuples if the merged histogram and the NULLS do not cover
// the total number of distinct values
CDouble dDistinctRemain(0.0);
CDouble dFreqRemain(0.0);
if ((1 - CStatistics::DEpsilon > dFreqBuckets + dNullFrequency) &&
(0 < dDistinct - dNDVBuckets - iNullNDV))
{
dDistinctRemain = std::max(CDouble(0.0), (dDistinct - dNDVBuckets - iNullNDV));
dFreqRemain = std::max(CDouble(0.0), (1 - dFreqBuckets - dNullFrequency));
}
CUtils::AddRefAppend(pdrgpdxlbucket, pdrgpdxlbucketTransformed);
// free up allocated datum and float4 arrays
gpdb::FreeAttrStatsSlot(oidAttType, pdrgdatumMCVValues, iNumMCVValues, pdrgfMCVFrequencies, iNumMCVFrequencies);
gpdb::FreeAttrStatsSlot(oidAttType, pdrgdatumHistValues, iNumHistValues, NULL, 0);
gpdb::FreeHeapTuple(heaptupleStats);
pdrgpdxlbucketTransformed->Release();
// create col stats object
pmdidColStats->AddRef();
CDXLColStats *pdxlcolstats = GPOS_NEW(pmp) CDXLColStats
(
pmp,
pmdidColStats,
pmdnameCol,
dWidth,
dNullFrequency,
dDistinctRemain,
dFreqRemain,
pdrgpdxlbucket,
false /* fColStatsMissing */
);
return pdxlcolstats;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdxlcolstatsSystemColumn
//
// @doc:
// Generate statistics for the system level columns
//
//---------------------------------------------------------------------------
CDXLColStats *
CTranslatorRelcacheToDXL::PdxlcolstatsSystemColumn
(
IMemoryPool *pmp,
OID oidRelation,
CMDIdColStats *pmdidColStats,
CMDName *pmdnameCol,
OID oidAttType,
AttrNumber attrnum,
DrgPdxlbucket *pdrgpdxlbucket,
CDouble dRows
)
{
GPOS_ASSERT(NULL != pmdidColStats);
GPOS_ASSERT(NULL != pmdnameCol);
GPOS_ASSERT(InvalidOid != oidAttType);
GPOS_ASSERT(0 > attrnum);
GPOS_ASSERT(NULL != pdrgpdxlbucket);
CMDIdGPDB *pmdidAttType = GPOS_NEW(pmp) CMDIdGPDB(oidAttType);
IMDType *pmdtype = Pmdtype(pmp, pmdidAttType);
GPOS_ASSERT(pmdtype->FFixedLength());
BOOL fColStatsMissing = true;
CDouble dNullFrequency(0.0);
CDouble dWidth(pmdtype->UlLength());
CDouble dDistinctRemain(0.0);
CDouble dFreqRemain(0.0);
if (CStatistics::DMinRows <= dRows)
{
switch(attrnum)
{
case GpSegmentIdAttributeNumber: // gp_segment_id
{
fColStatsMissing = false;
dFreqRemain = CDouble(1.0);
dDistinctRemain = CDouble(gpdb::UlSegmentCountGP());
break;
}
case TableOidAttributeNumber: // tableoid
{
fColStatsMissing = false;
dFreqRemain = CDouble(1.0);
dDistinctRemain = CDouble(UlTableCount(oidRelation));
break;
}
case SelfItemPointerAttributeNumber: // ctid
{
fColStatsMissing = false;
dFreqRemain = CDouble(1.0);
dDistinctRemain = dRows;
break;
}
default:
break;
}
}
// cleanup
pmdidAttType->Release();
pmdtype->Release();
return GPOS_NEW(pmp) CDXLColStats
(
pmp,
pmdidColStats,
pmdnameCol,
dWidth,
dNullFrequency,
dDistinctRemain,
dFreqRemain,
pdrgpdxlbucket,
fColStatsMissing
);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::UlTableCount
//
// @doc:
// For non-leaf partition tables return the number of child partitions
// else return 1
//
//---------------------------------------------------------------------------
ULONG
CTranslatorRelcacheToDXL::UlTableCount
(
OID oidRelation
)
{
GPOS_ASSERT(InvalidOid != oidRelation);
ULONG ulTableCount = ULONG_MAX;
if (gpdb::FRelPartIsNone(oidRelation))
{
// not a partitioned table
ulTableCount = 1;
}
else if (gpdb::FLeafPartition(oidRelation))
{
// leaf partition
ulTableCount = 1;
}
else
{
ulTableCount = gpdb::UlLeafPartitions(oidRelation);
}
GPOS_ASSERT(ULONG_MAX != ulTableCount);
return ulTableCount;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PimdobjCast
//
// @doc:
// Retrieve a cast function from relcache
//
//---------------------------------------------------------------------------
IMDCacheObject *
CTranslatorRelcacheToDXL::PimdobjCast
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
CMDIdCast *pmdidCast = CMDIdCast::PmdidConvert(pmdid);
IMDId *pmdidSrc = pmdidCast->PmdidSrc();
IMDId *pmdidDest = pmdidCast->PmdidDest();
OID oidSrc = CMDIdGPDB::PmdidConvert(pmdidSrc)->OidObjectId();
OID oidDest = CMDIdGPDB::PmdidConvert(pmdidDest)->OidObjectId();
OID oidCastFunc = 0;
BOOL fBinaryCoercible = false;
BOOL fCastExists = gpdb::FCastFunc(oidSrc, oidDest, &fBinaryCoercible, &oidCastFunc);
if (!fCastExists)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CHAR *szFuncName = NULL;
if (InvalidOid != oidCastFunc)
{
szFuncName = gpdb::SzFuncName(oidCastFunc);
}
else
{
// no explicit cast function: use the destination type name as the cast name
szFuncName = gpdb::SzTypeName(oidDest);
}
if (NULL == szFuncName)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
pmdid->AddRef();
pmdidSrc->AddRef();
pmdidDest->AddRef();
CMDName *pmdname = CDXLUtils::PmdnameFromSz(pmp, szFuncName);
CMDIdGPDB *pmdidCastFunc = CTranslatorUtils::PmdidWithVersion(pmp, oidCastFunc);
return GPOS_NEW(pmp) CMDCastGPDB(pmp, pmdid, pmdname, pmdidSrc, pmdidDest, fBinaryCoercible, pmdidCastFunc);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdobjScCmp
//
// @doc:
// Retrieve a scalar comparison from relcache
//
//---------------------------------------------------------------------------
IMDCacheObject *
CTranslatorRelcacheToDXL::PmdobjScCmp
(
IMemoryPool *pmp,
IMDId *pmdid
)
{
CMDIdScCmp *pmdidScCmp = CMDIdScCmp::PmdidConvert(pmdid);
IMDId *pmdidLeft = pmdidScCmp->PmdidLeft();
IMDId *pmdidRight = pmdidScCmp->PmdidRight();
IMDType::ECmpType ecmpt = pmdidScCmp->Ecmpt();
OID oidLeft = CMDIdGPDB::PmdidConvert(pmdidLeft)->OidObjectId();
OID oidRight = CMDIdGPDB::PmdidConvert(pmdidRight)->OidObjectId();
CmpType cmpt = (CmpType) UlCmpt(ecmpt);
OID oidScCmp = gpdb::OidScCmp(oidLeft, oidRight, cmpt);
if (InvalidOid == oidScCmp)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
CHAR *szName = gpdb::SzOpName(oidScCmp);
if (NULL == szName)
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDCacheEntryNotFound, pmdid->Wsz());
}
pmdid->AddRef();
pmdidLeft->AddRef();
pmdidRight->AddRef();
CMDName *pmdname = CDXLUtils::PmdnameFromSz(pmp, szName);
CMDIdGPDB *pmdidCmp = CTranslatorUtils::PmdidWithVersion(pmp, oidScCmp);
return GPOS_NEW(pmp) CMDScCmpGPDB(pmp, pmdid, pmdname, pmdidLeft, pmdidRight, ecmpt, pmdidCmp);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpdxlbucketTransformStats
//
// @doc:
// transform stats from pg_stats form to optimizer's preferred form
//
//---------------------------------------------------------------------------
DrgPdxlbucket *
CTranslatorRelcacheToDXL::PdrgpdxlbucketTransformStats
(
IMemoryPool *pmp,
OID oidAttType,
CDouble dDistinct,
CDouble dNullFreq,
const Datum *pdrgdatumMCVValues,
const float4 *pdrgfMCVFrequencies,
ULONG ulNumMCVValues,
const Datum *pdrgdatumHistValues,
ULONG ulNumHistValues
)
{
CMDIdGPDB *pmdidAttType = CTranslatorUtils::PmdidWithVersion(pmp, oidAttType);
IMDType *pmdtype = Pmdtype(pmp, pmdidAttType);
// translate MCVs to Orca histogram. Create an empty histogram if there are no MCVs.
CHistogram *phistGPDBMCV = PhistTransformGPDBMCV
(
pmp,
pmdtype,
pdrgdatumMCVValues,
pdrgfMCVFrequencies,
ulNumMCVValues
);
GPOS_ASSERT(phistGPDBMCV->FValid());
CDouble dMCVFreq = phistGPDBMCV->DFrequency();
BOOL fHasMCV = 0 < ulNumMCVValues && CStatistics::DEpsilon < dMCVFreq;
CDouble dHistFreq = 0.0;
if (1 < ulNumHistValues)
{
dHistFreq = CDouble(1.0) - dNullFreq - dMCVFreq;
}
BOOL fHasHist = 1 < ulNumHistValues && CStatistics::DEpsilon < dHistFreq;
CHistogram *phistGPDBHist = NULL;
// if histogram has any significant information, then extract it
if (fHasHist)
{
// histogram from gpdb histogram
phistGPDBHist = PhistTransformGPDBHist
(
pmp,
pmdtype,
pdrgdatumHistValues,
ulNumHistValues,
dDistinct,
dHistFreq
);
}
DrgPdxlbucket *pdrgpdxlbucket = NULL;
if (fHasHist && !fHasMCV)
{
// if histogram exists and dominates, use histogram only
pdrgpdxlbucket = Pdrgpdxlbucket(pmp, pmdtype, phistGPDBHist);
}
else if (!fHasHist && fHasMCV)
{
// if MCVs exist and dominate, use MCVs only
pdrgpdxlbucket = Pdrgpdxlbucket(pmp, pmdtype, phistGPDBMCV);
}
else if (fHasHist && fHasMCV)
{
// both histogram and MCVs exist and have significant info, merge MCV and histogram buckets
CHistogram *phistMerged = CStatisticsUtils::PhistMergeMcvHist(pmp, phistGPDBMCV, phistGPDBHist);
pdrgpdxlbucket = Pdrgpdxlbucket(pmp, pmdtype, phistMerged);
GPOS_DELETE(phistMerged);
}
else
{
// no MCVs nor histogram
GPOS_ASSERT(!fHasHist && !fHasMCV);
pdrgpdxlbucket = GPOS_NEW(pmp) DrgPdxlbucket(pmp);
}
// cleanup
pmdidAttType->Release();
pmdtype->Release();
GPOS_DELETE(phistGPDBMCV);
if (NULL != phistGPDBHist)
{
GPOS_DELETE(phistGPDBHist);
}
return pdrgpdxlbucket;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PhistTransformGPDBMCV
//
// @doc:
// Transform gpdb's mcv info to optimizer histogram
//
//---------------------------------------------------------------------------
CHistogram *
CTranslatorRelcacheToDXL::PhistTransformGPDBMCV
(
IMemoryPool *pmp,
const IMDType *pmdtype,
const Datum *pdrgdatumMCVValues,
const float4 *pdrgfMCVFrequencies,
ULONG ulNumMCVValues
)
{
DrgPdatum *pdrgpdatum = GPOS_NEW(pmp) DrgPdatum(pmp);
DrgPdouble *pdrgpdFreq = GPOS_NEW(pmp) DrgPdouble(pmp);
for (ULONG ul = 0; ul < ulNumMCVValues; ul++)
{
Datum datumMCV = pdrgdatumMCVValues[ul];
IDatum *pdatum = CTranslatorScalarToDXL::Pdatum(pmp, pmdtype, false /* fNull */, datumMCV);
pdrgpdatum->Append(pdatum);
pdrgpdFreq->Append(GPOS_NEW(pmp) CDouble(pdrgfMCVFrequencies[ul]));
if (!pdatum->FStatsComparable(pdatum))
{
// if less than operation is not supported on this datum, then no point
// building a histogram. return an empty histogram
pdrgpdatum->Release();
pdrgpdFreq->Release();
return GPOS_NEW(pmp) CHistogram(GPOS_NEW(pmp) DrgPbucket(pmp));
}
}
CHistogram *phist = CStatisticsUtils::PhistTransformMCV
(
pmp,
pmdtype,
pdrgpdatum,
pdrgpdFreq,
ulNumMCVValues
);
pdrgpdatum->Release();
pdrgpdFreq->Release();
return phist;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PhistTransformGPDBHist
//
// @doc:
// Transform GPDB's hist info to optimizer's histogram
//
//---------------------------------------------------------------------------
CHistogram *
CTranslatorRelcacheToDXL::PhistTransformGPDBHist
(
IMemoryPool *pmp,
const IMDType *pmdtype,
const Datum *pdrgdatumHistValues,
ULONG ulNumHistValues,
CDouble dDistinctHist,
CDouble dFreqHist
)
{
GPOS_ASSERT(1 < ulNumHistValues);
ULONG ulNumBuckets = ulNumHistValues - 1;
CDouble dDistinctPerBucket = dDistinctHist / CDouble(ulNumBuckets);
CDouble dFreqPerBucket = dFreqHist / CDouble(ulNumBuckets);
const ULONG ulBuckets = ulNumHistValues - 1;
// create buckets
DrgPbucket *pdrgppbucket = GPOS_NEW(pmp) DrgPbucket(pmp);
for (ULONG ul = 0; ul < ulBuckets; ul++)
{
Datum datumMin = pdrgdatumHistValues[ul];
IDatum *pdatumMin = CTranslatorScalarToDXL::Pdatum(pmp, pmdtype, false /* fNull */, datumMin);
Datum datumMax = pdrgdatumHistValues[ul + 1];
IDatum *pdatumMax = CTranslatorScalarToDXL::Pdatum(pmp, pmdtype, false /* fNull */, datumMax);
BOOL fLowerClosed = true; // GPDB histograms assumes lower bound to be closed
BOOL fUpperClosed = false; // GPDB histograms assumes upper bound to be open
if (ul == ulBuckets - 1)
{
// last bucket upper bound is also closed
fUpperClosed = true;
}
CBucket *pbucket = GPOS_NEW(pmp) CBucket
(
GPOS_NEW(pmp) CPoint(pdatumMin),
GPOS_NEW(pmp) CPoint(pdatumMax),
fLowerClosed,
fUpperClosed,
dFreqPerBucket,
dDistinctPerBucket
);
pdrgppbucket->Append(pbucket);
if (!pdatumMin->FStatsComparable(pdatumMin) || !pdatumMin->FStatsLessThan(pdatumMax))
{
// if less than operation is not supported on this datum,
// or the translated histogram does not conform to GPDB sort order (e.g. text column in Linux platform),
// then no point building a histogram. return an empty histogram
// TODO: shene 03/01/2014 translate histogram into Orca even if sort
// order is different in GPDB, and use const expression eval to compare
// datums in Orca (MPP-22780)
pdrgppbucket->Release();
return GPOS_NEW(pmp) CHistogram(GPOS_NEW(pmp) DrgPbucket(pmp));
}
}
CHistogram *phist = GPOS_NEW(pmp) CHistogram(pdrgppbucket);
return phist;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Pdrgpdxlbucket
//
// @doc:
// Histogram to array of dxl buckets
//
//---------------------------------------------------------------------------
DrgPdxlbucket *
CTranslatorRelcacheToDXL::Pdrgpdxlbucket
(
IMemoryPool *pmp,
const IMDType *pmdtype,
const CHistogram *phist
)
{
DrgPdxlbucket *pdrgpdxlbucket = GPOS_NEW(pmp) DrgPdxlbucket(pmp);
const DrgPbucket *pdrgpbucket = phist->Pdrgpbucket();
ULONG ulNumBuckets = pdrgpbucket->UlLength();
for (ULONG ul = 0; ul < ulNumBuckets; ul++)
{
CBucket *pbucket = (*pdrgpbucket)[ul];
IDatum *pdatumLB = pbucket->PpLower()->Pdatum();
CDXLDatum *pdxldatumLB = pmdtype->Pdxldatum(pmp, pdatumLB);
IDatum *pdatumUB = pbucket->PpUpper()->Pdatum();
CDXLDatum *pdxldatumUB = pmdtype->Pdxldatum(pmp, pdatumUB);
CDXLBucket *pdxlbucket = GPOS_NEW(pmp) CDXLBucket
(
pdxldatumLB,
pdxldatumUB,
pbucket->FLowerClosed(),
pbucket->FUpperClosed(),
pbucket->DFrequency(),
pbucket->DDistinct()
);
pdrgpdxlbucket->Append(pdxlbucket);
}
return pdrgpdxlbucket;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Erelstorage
//
// @doc:
// Get relation storage type
//
//---------------------------------------------------------------------------
IMDRelation::Erelstoragetype
CTranslatorRelcacheToDXL::Erelstorage
(
CHAR cStorageType
)
{
IMDRelation::Erelstoragetype erelstorage = IMDRelation::ErelstorageSentinel;
switch (cStorageType)
{
case RELSTORAGE_HEAP:
erelstorage = IMDRelation::ErelstorageHeap;
break;
case RELSTORAGE_AOROWS:
erelstorage = IMDRelation::ErelstorageAppendOnlyRows;
break;
case RELSTORAGE_PARQUET:
erelstorage = IMDRelation::ErelstorageAppendOnlyParquet;
break;
case RELSTORAGE_VIRTUAL:
erelstorage = IMDRelation::ErelstorageVirtual;
break;
case RELSTORAGE_EXTERNAL:
erelstorage = IMDRelation::ErelstorageExternal;
break;
default:
GPOS_ASSERT(!"Unsupported relation type");
}
return erelstorage;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpulPartKeys
//
// @doc:
// Get partition keys for relation or NULL if relation not partitioned.
// Caller responsible for closing the relation if an exception is raised
//
//---------------------------------------------------------------------------
DrgPul *
CTranslatorRelcacheToDXL::PdrgpulPartKeys
(
IMemoryPool *pmp,
Relation rel,
OID oid
)
{
GPOS_ASSERT(NULL != rel);
if (!gpdb::FRelPartIsRoot(oid))
{
// not a partitioned table
return NULL;
}
// TODO: antovl - Feb 23, 2012; support intermediate levels
DrgPul *pdrgpulPartKeys = GPOS_NEW(pmp) DrgPul(pmp);
MemoryContext mcxt = CurrentMemoryContext;
PartitionNode *pn = gpdb::PpnParts(oid, 0 /*level*/, 0 /*parent*/, false /*inctemplate*/, mcxt, true /*includesubparts*/);
GPOS_ASSERT(NULL != pn);
if (gpdb::FHashPartitioned(pn->part->parkind))
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Hash partitioning"));
}
List *plPartKeys = gpdb::PlPartitionAttrs(oid);
ListCell *plc = NULL;
ForEach (plc, plPartKeys)
{
List *plPartKey = (List *) lfirst(plc);
if (1 < gpdb::UlListLength(plPartKey))
{
GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, GPOS_WSZ_LIT("Composite part key"));
}
INT iAttno = linitial_int(plPartKey);
GPOS_ASSERT(0 < iAttno);
pdrgpulPartKeys->Append(GPOS_NEW(pmp) ULONG(iAttno - 1));
}
return pdrgpulPartKeys;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PulAttnoMapping
//
// @doc:
// Construct a mapping for GPDB attnos to positions in the columns array
//
//---------------------------------------------------------------------------
ULONG *
CTranslatorRelcacheToDXL::PulAttnoMapping
(
IMemoryPool *pmp,
DrgPmdcol *pdrgpmdcol,
ULONG ulMaxCols
)
{
GPOS_ASSERT(NULL != pdrgpmdcol);
GPOS_ASSERT(0 < pdrgpmdcol->UlLength());
GPOS_ASSERT(ulMaxCols > pdrgpmdcol->UlLength());
// build a mapping for attnos->positions
const ULONG ulCols = pdrgpmdcol->UlLength();
ULONG *pul = GPOS_NEW_ARRAY(pmp, ULONG, ulMaxCols);
// initialize all positions to ULONG_MAX
for (ULONG ul = 0; ul < ulMaxCols; ul++)
{
pul[ul] = ULONG_MAX;
}
for (ULONG ul = 0; ul < ulCols; ul++)
{
const IMDColumn *pmdcol = (*pdrgpmdcol)[ul];
INT iAttno = pmdcol->IAttno();
ULONG ulIndex = (ULONG) (GPDXL_SYSTEM_COLUMNS + iAttno);
pul[ulIndex] = ul;
}
return pul;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpdrgpulKeys
//
// @doc:
// Get key sets for relation
//
//---------------------------------------------------------------------------
DrgPdrgPul *
CTranslatorRelcacheToDXL::PdrgpdrgpulKeys
(
IMemoryPool *pmp,
OID oid,
BOOL fAddDefaultKeys,
BOOL fPartitioned,
ULONG *pulMapping
)
{
DrgPdrgPul *pdrgpdrgpul = GPOS_NEW(pmp) DrgPdrgPul(pmp);
List *plKeys = gpdb::PlRelationKeys(oid);
ListCell *plcKey = NULL;
ForEach (plcKey, plKeys)
{
List *plKey = (List *) lfirst(plcKey);
DrgPul *pdrgpulKey = GPOS_NEW(pmp) DrgPul(pmp);
ListCell *plcKeyElem = NULL;
ForEach (plcKeyElem, plKey)
{
INT iKey = lfirst_int(plcKeyElem);
ULONG ulPos = UlPosition(iKey, pulMapping);
pdrgpulKey->Append(GPOS_NEW(pmp) ULONG(ulPos));
}
GPOS_ASSERT(0 < pdrgpulKey->UlLength());
pdrgpdrgpul->Append(pdrgpulKey);
}
// add {segid, ctid} as a key
if (fAddDefaultKeys)
{
DrgPul *pdrgpulKey = GPOS_NEW(pmp) DrgPul(pmp);
if (fPartitioned)
{
// TableOid is part of default key for partitioned tables
ULONG ulPosTableOid = UlPosition(TableOidAttributeNumber, pulMapping);
pdrgpulKey->Append(GPOS_NEW(pmp) ULONG(ulPosTableOid));
}
ULONG ulPosSegid= UlPosition(GpSegmentIdAttributeNumber, pulMapping);
ULONG ulPosCtid = UlPosition(SelfItemPointerAttributeNumber, pulMapping);
pdrgpulKey->Append(GPOS_NEW(pmp) ULONG(ulPosSegid));
pdrgpulKey->Append(GPOS_NEW(pmp) ULONG(ulPosCtid));
pdrgpdrgpul->Append(pdrgpulKey);
}
return pdrgpdrgpul;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::NormalizeFrequencies
//
// @doc:
// Sometimes a set of frequencies can add up to more than 1.0.
// Fix these cases
//
//---------------------------------------------------------------------------
void
CTranslatorRelcacheToDXL::NormalizeFrequencies
(
float4 *pdrgf,
ULONG ulLength,
CDouble *pdNullFrequency
)
{
if (ulLength == 0 && (*pdNullFrequency) < 1.0)
{
return;
}
CDouble dTotal = *pdNullFrequency;
for (ULONG ul = 0; ul < ulLength; ul++)
{
dTotal = dTotal + CDouble(pdrgf[ul]);
}
if (dTotal > CDouble(1.0))
{
float4 fDenom = (float4) (dTotal + CStatistics::DEpsilon).DVal();
// divide all values by the total
for (ULONG ul = 0; ul < ulLength; ul++)
{
pdrgf[ul] = pdrgf[ul] / fDenom;
}
*pdNullFrequency = *pdNullFrequency / fDenom;
}
#ifdef GPOS_DEBUG
// recheck
CDouble dTotalRecheck = *pdNullFrequency;
for (ULONG ul = 0; ul < ulLength; ul++)
{
dTotalRecheck = dTotalRecheck + CDouble(pdrgf[ul]);
}
GPOS_ASSERT(dTotalRecheck <= CDouble(1.0));
#endif
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::FIndexSupported
//
// @doc:
// Check if index type is supported
//
//---------------------------------------------------------------------------
BOOL
CTranslatorRelcacheToDXL::FIndexSupported
(
Relation relIndex
)
{
HeapTupleData *pht = relIndex->rd_indextuple;
// index expressions and index constraints not supported
return gpdb::FHeapAttIsNull(pht, Anum_pg_index_indexprs) &&
gpdb::FHeapAttIsNull(pht, Anum_pg_index_indpred) &&
(BTREE_AM_OID == relIndex->rd_rel->relam || BITMAP_AM_OID == relIndex->rd_rel->relam);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdpartcnstrIndex
//
// @doc:
// Retrieve part constraint for index
//
//---------------------------------------------------------------------------
CMDPartConstraintGPDB *
CTranslatorRelcacheToDXL::PmdpartcnstrIndex
(
IMemoryPool *pmp,
CMDAccessor *pmda,
const IMDRelation *pmdrel,
Node *pnodePartCnstr,
DrgPul *pdrgpulDefaultParts,
BOOL fUnbounded
)
{
DrgPdxlcd *pdrgpdxlcd = GPOS_NEW(pmp) DrgPdxlcd(pmp);
const ULONG ulColumns = pmdrel->UlColumns();
for (ULONG ul = 0; ul < ulColumns; ul++)
{
const IMDColumn *pmdcol = pmdrel->Pmdcol(ul);
CMDName *pmdnameCol = GPOS_NEW(pmp) CMDName(pmp, pmdcol->Mdname().Pstr());
CMDIdGPDB *pmdidColType = CMDIdGPDB::PmdidConvert(pmdcol->PmdidType());
pmdidColType->AddRef();
// create a column descriptor for the column
CDXLColDescr *pdxlcd = GPOS_NEW(pmp) CDXLColDescr
(
pmp,
pmdnameCol,
ul + 1, // ulColId
pmdcol->IAttno(),
pmdidColType,
false // fColDropped
);
pdrgpdxlcd->Append(pdxlcd);
}
CMDPartConstraintGPDB *pmdpartcnstr = PmdpartcnstrFromNode(pmp, pmda, pdrgpdxlcd, pnodePartCnstr, pdrgpulDefaultParts, fUnbounded);
pdrgpdxlcd->Release();
return pmdpartcnstr;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdpartcnstrRelation
//
// @doc:
// Retrieve part constraint for relation
//
//---------------------------------------------------------------------------
CMDPartConstraintGPDB *
CTranslatorRelcacheToDXL::PmdpartcnstrRelation
(
IMemoryPool *pmp,
CMDAccessor *pmda,
OID oidRel,
DrgPmdcol *pdrgpmdcol
)
{
// get the part constraints
List *plDefaultLevelsRel = NIL;
Node *pnode = gpdb::PnodePartConstraintRel(oidRel, &plDefaultLevelsRel);
List *plPartKeys = gpdb::PlPartitionAttrs(oidRel);
const ULONG ulLevels = gpdb::UlListLength(plPartKeys);
gpdb::FreeList(plPartKeys);
BOOL fUnbounded = true;
DrgPul *pdrgpulDefaultLevels = GPOS_NEW(pmp) DrgPul(pmp);
for (ULONG ul = 0; ul < ulLevels; ul++)
{
if (FDefaultPartition(plDefaultLevelsRel, ul))
{
pdrgpulDefaultLevels->Append(GPOS_NEW(pmp) ULONG(ul));
}
else
{
fUnbounded = false;
}
}
DrgPdxlcd *pdrgpdxlcd = GPOS_NEW(pmp) DrgPdxlcd(pmp);
const ULONG ulColumns = pdrgpmdcol->UlLength();
for (ULONG ul = 0; ul < ulColumns; ul++)
{
const IMDColumn *pmdcol = (*pdrgpmdcol)[ul];
CMDName *pmdnameCol = GPOS_NEW(pmp) CMDName(pmp, pmdcol->Mdname().Pstr());
CMDIdGPDB *pmdidColType = CMDIdGPDB::PmdidConvert(pmdcol->PmdidType());
pmdidColType->AddRef();
// create a column descriptor for the column
CDXLColDescr *pdxlcd = GPOS_NEW(pmp) CDXLColDescr
(
pmp,
pmdnameCol,
ul + 1, // ulColId
pmdcol->IAttno(),
pmdidColType,
false // fColDropped
);
pdrgpdxlcd->Append(pdxlcd);
}
CMDPartConstraintGPDB *pmdpartcnstr = PmdpartcnstrFromNode(pmp, pmda, pdrgpdxlcd, pnode, pdrgpulDefaultLevels, fUnbounded);
pdrgpulDefaultLevels->Release();
pdrgpdxlcd->Release();
return pmdpartcnstr;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PmdpartcnstrFromNode
//
// @doc:
// Retrieve part constraint from GPDB node
//
//---------------------------------------------------------------------------
CMDPartConstraintGPDB *
CTranslatorRelcacheToDXL::PmdpartcnstrFromNode
(
IMemoryPool *pmp,
CMDAccessor *pmda,
DrgPdxlcd *pdrgpdxlcd,
Node *pnodeCnstr,
DrgPul *pdrgpulDefaultParts,
BOOL fUnbounded
)
{
if (NULL == pnodeCnstr)
{
return NULL;
}
CTranslatorScalarToDXL sctranslator
(
pmp,
pmda,
NULL, // pulidgtorCol
NULL, // pulidgtorCTE
0, // ulQueryLevel
true, // m_fQuery
NULL, // m_pplstmt
NULL, // m_pmappv
NULL, // phmulCTEEntries
NULL // pdrgpdxlnCTE
);
// generate a mock mapping between var to column information
CMappingVarColId *pmapvarcolid = GPOS_NEW(pmp) CMappingVarColId(pmp);
pmapvarcolid->LoadColumns(0 /*ulQueryLevel */, 1 /* rteIndex */, pdrgpdxlcd);
// translate the check constraint expression
CDXLNode *pdxlnScalar = sctranslator.PdxlnScOpFromExpr((Expr *) pnodeCnstr, pmapvarcolid);
// cleanup
GPOS_DELETE(pmapvarcolid);
pdrgpulDefaultParts->AddRef();
return GPOS_NEW(pmp) CMDPartConstraintGPDB(pmp, pdxlnScalar, pdrgpulDefaultParts, fUnbounded);
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::FHasSystemColumns
//
// @doc:
// Does given relation type have system columns.
// Currently only regular relations, sequences, toast values relations and
// AO segment relations have system columns
//
//---------------------------------------------------------------------------
BOOL
CTranslatorRelcacheToDXL::FHasSystemColumns
(
char cRelKind
)
{
return RELKIND_RELATION == cRelKind ||
RELKIND_SEQUENCE == cRelKind ||
RELKIND_AOSEGMENTS == cRelKind ||
RELKIND_TOASTVALUE == cRelKind;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::Ecmpt
//
// @doc:
// Translate GPDB comparison types into optimizer comparison types
//
//---------------------------------------------------------------------------
IMDType::ECmpType
CTranslatorRelcacheToDXL::Ecmpt
(
ULONG ulCmpt
)
{
for (ULONG ul = 0; ul < GPOS_ARRAY_SIZE(rgulCmpTypeMappings); ul++)
{
const ULONG *pul = rgulCmpTypeMappings[ul];
if (pul[1] == ulCmpt)
{
return (IMDType::ECmpType) pul[0];
}
}
return IMDType::EcmptOther;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::UlCmpt
//
// @doc:
// Translate optimizer comparison types into GPDB comparison types
//
//---------------------------------------------------------------------------
ULONG
CTranslatorRelcacheToDXL::UlCmpt
(
IMDType::ECmpType ecmpt
)
{
for (ULONG ul = 0; ul < GPOS_ARRAY_SIZE(rgulCmpTypeMappings); ul++)
{
const ULONG *pul = rgulCmpTypeMappings[ul];
if (pul[0] == ecmpt)
{
return (ULONG) pul[1];
}
}
return CmptOther;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpmdidIndexOpClasses
//
// @doc:
// Retrieve the opclasses for the keys of the given index
//
//---------------------------------------------------------------------------
DrgPmdid *
CTranslatorRelcacheToDXL::PdrgpmdidIndexOpClasses
(
IMemoryPool *pmp,
IMDId *pmdidIndex
)
{
List *plOpClasses = gpdb::PlIndexOpClasses(CMDIdGPDB::PmdidConvert(pmdidIndex)->OidObjectId());
DrgPmdid *pdrgpmdid = GPOS_NEW(pmp) DrgPmdid(pmp);
ListCell *plc = NULL;
ForEach(plc, plOpClasses)
{
OID oidOpClass = lfirst_oid(plc);
CMDIdGPDB *pmdidOpClass = CTranslatorUtils::PmdidWithVersion(pmp, oidOpClass);
pdrgpmdid->Append(pmdidOpClass);
}
return pdrgpmdid;
}
//---------------------------------------------------------------------------
// @function:
// CTranslatorRelcacheToDXL::PdrgpmdidScOpOpClasses
//
// @doc:
// Retrieve the opclasses for the keys of the given scalar operator
//
//---------------------------------------------------------------------------
DrgPmdid *
CTranslatorRelcacheToDXL::PdrgpmdidScOpOpClasses
(
IMemoryPool *pmp,
IMDId *pmdidScOp
)
{
List *plOpClasses = gpdb::PlScOpOpClasses(CMDIdGPDB::PmdidConvert(pmdidScOp)->OidObjectId());
DrgPmdid *pdrgpmdid = GPOS_NEW(pmp) DrgPmdid(pmp);
ListCell *plc = NULL;
ForEach(plc, plOpClasses)
{
OID oidOpClass = lfirst_oid(plc);
CMDIdGPDB *pmdidOpClass = CTranslatorUtils::PmdidWithVersion(pmp, oidOpClass);
pdrgpmdid->Append(pmdidOpClass);
}
return pdrgpmdid;
}
// EOF