blob: aca95b2cc0a6592efb11b19fc564797b2302f619 [file] [log] [blame]
//---------------------------------------------------------------------------
// Greenplum Database
// Copyright (C) 2012 EMC Corp.
//
// @filename:
// gpdbwrappers.cpp
//
// @doc:
// Implementation of GPDB function wrappers. Note that we should never
// return directly from inside the PG_TRY() block, in order to restore
// the long jump stack. That is why we save the return value of the GPDB
// function to a local variable and return it after the PG_END_TRY().
// ./README file contains the sources (caches and catalog tables) of metadata
// requested by the optimizer and retrieved using GPDB function wrappers. Any
// change to optimizer's requested metadata should also be recorded in ./README file.
//
//
// @test:
//
//
//---------------------------------------------------------------------------
#include "gpopt/gpdbwrappers.h"
#include <limits> // std::numeric_limits
#include "gpos/base.h"
#include "gpos/error/CAutoExceptionStack.h"
#include "gpos/error/CException.h"
#include "gpopt/utils/gpdbdefs.h"
#include "naucrates/exception.h"
#include "catalog/pg_collation.h"
extern "C" {
#include "access/amapi.h"
#include "access/external.h"
#include "access/genam.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_inherits.h"
#include "foreign/fdwapi.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "optimizer/plancat.h"
#include "optimizer/prep.h"
#include "optimizer/subselect.h"
#include "parser/parse_agg.h"
#include "partitioning/partdesc.h"
#include "storage/lmgr.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
}
#define GP_WRAP_START \
sigjmp_buf local_sigjmp_buf; \
{ \
CAutoExceptionStack aes((void **) &PG_exception_stack, \
(void **) &error_context_stack); \
if (0 == sigsetjmp(local_sigjmp_buf, 0)) \
{ \
aes.SetLocalJmp(&local_sigjmp_buf)
#define GP_WRAP_END \
} \
else \
{ \
GPOS_RAISE(gpdxl::ExmaGPDB, gpdxl::ExmiGPDBError); \
} \
}
using namespace gpos;
bool
gpdb::AggregateExists(Oid oid)
{
GP_WRAP_START;
{
return aggregate_exists(oid);
}
GP_WRAP_END;
return false;
}
Bitmapset *
gpdb::BmsAddMember(Bitmapset *a, int x)
{
GP_WRAP_START;
{
return bms_add_member(a, x);
}
GP_WRAP_END;
return nullptr;
}
int
gpdb::BmsNextMember(const Bitmapset *a, int prevbit)
{
GP_WRAP_START;
{
return bms_next_member(a, prevbit);
}
GP_WRAP_END;
return -2;
}
void *
gpdb::CopyObject(void *from)
{
GP_WRAP_START;
{
return copyObjectImpl(from);
}
GP_WRAP_END;
return nullptr;
}
Size
gpdb::DatumSize(Datum value, bool type_by_val, int iTypLen)
{
GP_WRAP_START;
{
return datumGetSize(value, type_by_val, iTypLen);
}
GP_WRAP_END;
return 0;
}
Node *
gpdb::MutateExpressionTree(Node *node, Node *(*mutator)(Node *, void *), void *context)
{
GP_WRAP_START;
{
return expression_tree_mutator_wrapper(node, mutator, context);
}
GP_WRAP_END;
return nullptr;
}
bool
gpdb::WalkExpressionTree(Node *node, bool (*walker)(Node *, void *), void *context)
{
GP_WRAP_START;
{
return expression_tree_walker_wrapper(node, walker, context);
}
GP_WRAP_END;
return false;
}
gpos::BOOL
gpdb::WalkQueryTree(Query *query, bool (*walker)(), void *context, int flags)
{
GP_WRAP_START;
{
return query_tree_walker(query, walker, context, flags);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::ExprType(Node *expr)
{
GP_WRAP_START;
{
return exprType(expr);
}
GP_WRAP_END;
return 0;
}
int32
gpdb::ExprTypeMod(Node *expr)
{
GP_WRAP_START;
{
return exprTypmod(expr);
}
GP_WRAP_END;
return 0;
}
Oid
gpdb::ExprCollation(Node *expr)
{
GP_WRAP_START;
{
if (expr && IsA(expr, List))
{
// GPDB_91_MERGE_FIXME: collation
List *exprlist = (List *) expr;
ListCell *lc;
Oid collation = InvalidOid;
foreach (lc, exprlist)
{
Node *expr = (Node *) lfirst(lc);
if ((collation = exprCollation(expr)) != InvalidOid)
{
break;
}
}
return collation;
}
else
{
return exprCollation(expr);
}
}
GP_WRAP_END;
return 0;
}
Oid
gpdb::TypeCollation(Oid type)
{
GP_WRAP_START;
{
// The real oid returned by the get_typcollation function as the result
// Cancel the logic that used the value DEFAULT_COLLATION_OID
Oid typcollation = get_typcollation(type);
return OidIsValid(typcollation) ? typcollation : InvalidOid;
}
GP_WRAP_END;
return 0;
}
void
gpdb::TypLenByVal(Oid typid, int16 *typlen, bool *typbyval)
{
GP_WRAP_START;
{
get_typlenbyval(typid, typlen, typbyval);
}
GP_WRAP_END;
}
List *
gpdb::ExtractNodesPlan(Plan *pl, int node_tag, bool descend_into_subqueries)
{
GP_WRAP_START;
{
return extract_nodes_plan(pl, node_tag, descend_into_subqueries);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::ExtractNodesExpression(Node *node, int node_tag,
bool descend_into_subqueries)
{
GP_WRAP_START;
{
return extract_nodes_expression(node, node_tag,
descend_into_subqueries);
}
GP_WRAP_END;
return NIL;
}
void
gpdb::FreeAttrStatsSlot(AttStatsSlot *sslot)
{
GP_WRAP_START;
{
free_attstatsslot(sslot);
return;
}
GP_WRAP_END;
}
bool
gpdb::IsFuncAllowedForPartitionSelection(Oid funcid)
{
GP_WRAP_START;
switch (funcid)
{
// These are the functions we have allowed as lossy casts for Partition selection.
// For range partition selection, the logic in ORCA checks on bounds of the partition ranges.
// Hence these must be increasing functions.
case F_TIMESTAMP_DATE: // date(timestamp) -> date
case F_INT4_FLOAT8: // int4(float8) -> int4
case F_INT4_FLOAT4: // int4(float4) -> int4
case F_INT2_INT8: // int2(int8) -> int2
case F_INT4_INT8: // int4(int8) -> int4
case F_INT2_INT4: // int2(int4) -> int2
case F_INT8_FLOAT4: // int8(float4) -> int8
case F_INT2_FLOAT4: // int2(float4) -> int2
case F_NUMERIC_FLOAT4: // numeric(float4) -> numeric
case F_INT8_FLOAT8: // int8(float8) -> int8
case F_INT2_FLOAT8: // int2(float4) -> int2
case F_FLOAT4_FLOAT8: // float4(float8) -> float4
case F_FLOAT8_NUMERIC: // numeric(float8) -> numeric
case F_NUMERIC_INT8: // int8(numeric) -> int8
case F_NUMERIC_INT2: // int2(numeric) -> int2
case F_NUMERIC_INT4: // int4(numeric) -> int4
return true;
default:
return false;
}
GP_WRAP_END;
}
bool
gpdb::FuncStrict(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return func_strict(funcid);
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsFuncNDVPreserving(Oid funcid)
{
// Given a function oid, return whether it's one of a list of NDV-preserving
// functions (estimated NDV of output is similar to that of the input)
switch (funcid)
{
// for now, these are the functions we consider for this optimization
case F_LOWER_TEXT:
case F_LTRIM_TEXT:
case F_BTRIM_TEXT:
case F_RTRIM_TEXT:
case F_UPPER_TEXT:
return true;
default:
return false;
}
}
char
gpdb::FuncStability(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return func_volatile(funcid);
}
GP_WRAP_END;
return '\0';
}
RegProcedure
gpdb::FuncSupport(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return get_func_support(funcid);
}
GP_WRAP_END;
return InvalidOid;
}
Oid
gpdb::FuncNamespace(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return get_func_namespace(funcid);
}
GP_WRAP_END;
return InvalidOid;
}
char
gpdb::FuncExecLocation(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return func_exec_location(funcid);
}
GP_WRAP_END;
return '\0';
}
bool
gpdb::FunctionExists(Oid oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return function_exists(oid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetAggIntermediateResultType(Oid aggid)
{
GP_WRAP_START;
{
/* catalog tables: pg_aggregate */
return get_agg_transtype(aggid);
}
GP_WRAP_END;
return 0;
}
int
gpdb::GetAggregateArgTypes(Aggref *aggref, Oid *inputTypes)
{
GP_WRAP_START;
{
return get_aggregate_argtypes(aggref, inputTypes);
}
GP_WRAP_END;
return 0;
}
Oid
gpdb::ResolveAggregateTransType(Oid aggfnoid, Oid aggtranstype, Oid *inputTypes,
int numArguments)
{
GP_WRAP_START;
{
return resolve_aggregate_transtype(aggfnoid, aggtranstype, inputTypes,
numArguments);
}
GP_WRAP_END;
return 0;
}
static Datum
GetAggInitVal(Datum textInitVal, Oid transtype)
{
Oid typinput,
typioparam;
char *strInitVal;
Datum initVal;
getTypeInputInfo(transtype, &typinput, &typioparam);
strInitVal = TextDatumGetCString(textInitVal);
initVal = OidInputFunctionCall(typinput, strInitVal,
typioparam, -1);
pfree(strInitVal);
return initVal;
}
void
gpdb::GetAggregateInfo(Aggref *aggref, Oid *aggtransfn,
Oid *aggfinalfn, Oid *aggcombinefn,
Oid *aggserialfn, Oid *aggdeserialfn,
Oid *aggtranstype, int *aggtransspace,
Datum *initValue, bool *initValueIsNull,
bool *shareable)
{
GP_WRAP_START;
{
HeapTuple aggTuple;
Form_pg_aggregate aggform;
Datum textInitVal;
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
aggTuple = SearchSysCache1(AGGFNOID,
ObjectIdGetDatum(aggref->aggfnoid));
if (!HeapTupleIsValid(aggTuple))
elog(ERROR, "cache lookup failed for aggregate %u",
aggref->aggfnoid);
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
*aggtransfn = aggform->aggtransfn;
*aggfinalfn = aggform->aggfinalfn;
*aggcombinefn = aggform->aggcombinefn;
*aggserialfn = aggform->aggserialfn;
*aggdeserialfn = aggform->aggdeserialfn;
*aggtranstype = aggform->aggtranstype;
*aggtransspace = aggform->aggtransspace;
/*
* Resolve the possibly-polymorphic aggregate transition type.
*/
/* extract argument types (ignoring any ORDER BY expressions) */
numArguments = get_aggregate_argtypes(aggref, inputTypes);
/* resolve actual type of transition state, if polymorphic */
*aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid,
*aggtranstype,
inputTypes,
numArguments);
/* get initial value */
textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
Anum_pg_aggregate_agginitval,
initValueIsNull);
if (*initValueIsNull)
*initValue = (Datum) 0;
else
*initValue = GetAggInitVal(textInitVal, *aggtranstype);
/*
* If finalfn is marked read-write, we can't share transition states; but
* it is okay to share states for AGGMODIFY_SHAREABLE aggs.
*
* In principle, in a partial aggregate, we could share the transition
* state even if the final function is marked as read-write, because the
* partial aggregate doesn't execute the final function. But it's too
* early to know whether we're going perform a partial aggregate.
*/
*shareable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE);
ReleaseSysCache(aggTuple);
}
GP_WRAP_END;
}
int
gpdb::FindCompatibleAgg(List *agginfos, Aggref *newagg,
List **same_input_transnos)
{
GP_WRAP_START;
{
return find_compatible_agg(agginfos, newagg, same_input_transnos);
}
GP_WRAP_END;
return -1;
}
int
gpdb::FindCompatibleTrans(List *aggtransinfos, bool shareable,
Oid aggtransfn, Oid aggtranstype,
int transtypeLen, bool transtypeByVal,
Oid aggcombinefn, Oid aggserialfn,
Oid aggdeserialfn, Datum initValue,
bool initValueIsNull, List *transnos)
{
GP_WRAP_START;
{
return find_compatible_trans(aggtransinfos, shareable, aggtransfn,
aggtranstype, transtypeLen, transtypeByVal, aggcombinefn, aggserialfn,
aggdeserialfn, initValue, initValueIsNull, transnos);
}
GP_WRAP_END;
return -1;
}
Query *
gpdb::FlattenJoinAliasVar(Query *query, gpos::ULONG query_level)
{
GP_WRAP_START;
{
return flatten_join_alias_var_optimizer(query, query_level);
}
GP_WRAP_END;
return nullptr;
}
bool
gpdb::IsOrderedAgg(Oid aggid)
{
GP_WRAP_START;
{
/* catalog tables: pg_aggregate */
return is_agg_ordered(aggid);
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsRepSafeAgg(Oid aggid)
{
GP_WRAP_START;
{
/* catalog tables: pg_aggregate */
return is_agg_repsafe(aggid);
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsAggPartialCapable(Oid aggid)
{
GP_WRAP_START;
{
/* catalog tables: pg_aggregate */
return is_agg_partial_capable(aggid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetAggregate(const char *agg, Oid type_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_aggregate */
return get_aggregate(agg, type_oid);
}
GP_WRAP_END;
return 0;
}
Oid
gpdb::GetArrayType(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type */
return get_array_type(typid);
}
GP_WRAP_END;
return 0;
}
bool
gpdb::GetAttrStatsSlot(AttStatsSlot *sslot, HeapTuple statstuple, int reqkind,
Oid reqop, int flags)
{
GP_WRAP_START;
{
return get_attstatsslot(sslot, statstuple, reqkind, reqop, flags);
}
GP_WRAP_END;
return false;
}
HeapTuple
gpdb::GetAttStats(Oid relid, AttrNumber attnum)
{
GP_WRAP_START;
{
/* catalog tables: pg_statistic */
return get_att_stats(relid, attnum);
}
GP_WRAP_END;
return nullptr;
}
List *
gpdb::GetExtStats(Relation rel)
{
GP_WRAP_START;
{
/* catalog tables: pg_statistic_ext */
return GetRelationExtStatistics(rel);
}
GP_WRAP_END;
return nullptr;
}
char *
gpdb::GetExtStatsName(Oid statOid)
{
GP_WRAP_START;
{
return GetExtStatisticsName(statOid);
}
GP_WRAP_END;
return nullptr;
}
List *
gpdb::GetExtStatsKinds(Oid statOid)
{
GP_WRAP_START;
{
return GetExtStatisticsKinds(statOid);
}
GP_WRAP_END;
return nullptr;
}
Oid
gpdb::GetCommutatorOp(Oid opno)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator */
return get_commutator(opno);
}
GP_WRAP_END;
return 0;
}
char *
gpdb::GetCheckConstraintName(Oid check_constraint_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_constraint */
return get_check_constraint_name(check_constraint_oid);
}
GP_WRAP_END;
return nullptr;
}
Oid
gpdb::GetCheckConstraintRelid(Oid check_constraint_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_constraint */
return get_check_constraint_relid(check_constraint_oid);
}
GP_WRAP_END;
return 0;
}
Node *
gpdb::PnodeCheckConstraint(Oid check_constraint_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_constraint */
return get_check_constraint_expr_tree(check_constraint_oid);
}
GP_WRAP_END;
return nullptr;
}
List *
gpdb::GetCheckConstraintOids(Oid rel_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_constraint */
return get_check_constraint_oids(rel_oid);
}
GP_WRAP_END;
return nullptr;
}
Node *
gpdb::GetRelationPartConstraints(Relation rel)
{
GP_WRAP_START;
{
/* catalog tables: pg_partition, pg_partition_rule, pg_constraint */
List *part_quals = RelationGetPartitionQual(rel);
if (part_quals)
{
return (Node *) make_ands_explicit(part_quals);
}
}
GP_WRAP_END;
return nullptr;
}
PartitionKey
gpdb::GetRelationPartitionKey(Relation rel)
{
GP_WRAP_START;
{
return RelationGetPartitionKey(rel);
}
GP_WRAP_END;
return nullptr;
}
PartitionDesc
gpdb::RelationGetPartitionDesc(Relation rel, bool omit_detached)
{
GP_WRAP_START;
{
return ::RelationGetPartitionDesc(rel, omit_detached);
}
GP_WRAP_END;
return nullptr;
}
bool
gpdb::GetCastFunc(Oid src_oid, Oid dest_oid, bool *is_binary_coercible,
Oid *cast_fn_oid, CoercionPathType *pathtype)
{
GP_WRAP_START;
{
/* catalog tables: pg_cast */
return get_cast_func(src_oid, dest_oid, is_binary_coercible,
cast_fn_oid, pathtype);
}
GP_WRAP_END;
return false;
}
unsigned int
gpdb::GetComparisonType(Oid op_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_amop */
return get_comparison_type(op_oid);
}
GP_WRAP_END;
return CmptOther;
}
Oid
gpdb::GetComparisonOperator(Oid left_oid, Oid right_oid, unsigned int cmpt)
{
GP_WRAP_START;
{
#ifdef FAULT_INJECTOR
SIMPLE_FAULT_INJECTOR("gpdbwrappers_get_comparison_operator");
#endif
/* catalog tables: pg_amop */
return get_comparison_operator(left_oid, right_oid, (CmpType) cmpt);
}
GP_WRAP_END;
return InvalidOid;
}
Oid
gpdb::GetEqualityOp(Oid type_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type */
Oid eq_opr;
get_sort_group_operators(type_oid, false, true, false, nullptr, &eq_opr,
nullptr, nullptr);
return eq_opr;
}
GP_WRAP_END;
return InvalidOid;
}
Oid
gpdb::GetEqualityOpForOrderingOp(Oid opno, bool *reverse)
{
GP_WRAP_START;
{
/* catalog tables: pg_amop */
return get_equality_op_for_ordering_op(opno, reverse);
}
GP_WRAP_END;
return InvalidOid;
}
Oid
gpdb::GetOrderingOpForEqualityOp(Oid opno, bool *reverse)
{
GP_WRAP_START;
{
/* catalog tables: pg_amop */
return get_ordering_op_for_equality_op(opno, reverse);
}
GP_WRAP_END;
return InvalidOid;
}
char *
gpdb::GetFuncName(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return get_func_name(funcid);
}
GP_WRAP_END;
return nullptr;
}
List *
gpdb::GetFuncOutputArgTypes(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return get_func_output_arg_types(funcid);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::ProcessRecordFuncTargetList(Oid funcid, List *targetList)
{
GP_WRAP_START;
{
HeapTuple tp;
int numargs;
Oid *argtypes = NULL;
char **argnames = NULL;
char *argmodes = NULL;
int i;
Datum datum;
bool isNull;
Oid prorettype;
ListCell *lc = NULL;
int index;
tp = SearchSysCache1(PROCOID,
ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", funcid);
datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prorettype, &isNull);
prorettype = DatumGetObjectId(datum);
ReleaseSysCache(tp);
if (prorettype != RECORDOID)
return targetList;
numargs = get_func_arg_info(tp, &argtypes, &argnames, &argmodes);
if (numargs > 0 && argtypes && argnames && argmodes)
{
foreach (lc, targetList)
{
index = 0;
TargetEntry *target_entry = (TargetEntry *) lfirst(lc);
for (i = 0; i < numargs; i++)
{
if (PROARGMODE_INOUT == argmodes[i] || PROARGMODE_OUT == argmodes[i] || PROARGMODE_TABLE == argmodes[i])
index++;
if (!strcmp(target_entry->resname, argnames[i]) &&
(PROARGMODE_INOUT == argmodes[i] || PROARGMODE_OUT == argmodes[i] || PROARGMODE_TABLE == argmodes[i]))
{
target_entry->resno = index;
break;
}
}
}
}
}
GP_WRAP_END;
return targetList;
}
List *
gpdb::GetFuncArgTypes(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return get_func_arg_types(funcid);
}
GP_WRAP_END;
return NIL;
}
bool
gpdb::GetFuncRetset(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return get_func_retset(funcid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetFuncRetType(Oid funcid)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return get_func_rettype(funcid);
}
GP_WRAP_END;
return 0;
}
Oid
gpdb::GetInverseOp(Oid opno)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator */
return get_negator(opno);
}
GP_WRAP_END;
return 0;
}
RegProcedure
gpdb::GetOpFunc(Oid opno)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator */
return get_opcode(opno);
}
GP_WRAP_END;
return 0;
}
char *
gpdb::GetOpName(Oid opno)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator */
return get_opname(opno);
}
GP_WRAP_END;
return nullptr;
}
List *
gpdb::GetRelationKeys(Oid relid)
{
GP_WRAP_START;
{
/* catalog tables: pg_constraint */
return get_relation_keys(relid);
}
GP_WRAP_END;
return NIL;
}
Oid
gpdb::GetTypeRelid(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type */
return get_typ_typrelid(typid);
}
GP_WRAP_END;
return 0;
}
char *
gpdb::GetTypeName(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type */
return get_type_name(typid);
}
GP_WRAP_END;
return nullptr;
}
int
gpdb::GetGPSegmentCount(void)
{
GP_WRAP_START;
{
return getgpsegmentCount();
}
GP_WRAP_END;
return 0;
}
bool
gpdb::HeapAttIsNull(HeapTuple tup, int attno)
{
GP_WRAP_START;
{
return heap_attisnull(tup, attno, nullptr);
}
GP_WRAP_END;
return false;
}
void
gpdb::FreeHeapTuple(HeapTuple htup)
{
GP_WRAP_START;
{
heap_freetuple(htup);
return;
}
GP_WRAP_END;
}
Oid
gpdb::GetDefaultDistributionOpclassForType(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type, pg_opclass */
return cdb_default_distribution_opclass_for_type(typid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetColumnDefOpclassForType(List *opclassName, Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type, pg_opclass */
return cdb_get_opclass_for_column_def(opclassName, typid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetDefaultDistributionOpfamilyForType(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type, pg_opclass */
return cdb_default_distribution_opfamily_for_type(typid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetDefaultPartitionOpfamilyForType(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type, pg_opclass */
return default_partition_opfamily_for_type(typid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetHashProcInOpfamily(Oid opfamily, Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_amproc, pg_type, pg_opclass */
return cdb_hashproc_in_opfamily(opfamily, typid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::IsLegacyCdbHashFunction(Oid funcid)
{
GP_WRAP_START;
{
return isLegacyCdbHashFunction(funcid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetLegacyCdbHashOpclassForBaseType(Oid typid)
{
GP_WRAP_START;
{
return get_legacy_cdbhash_opclass_for_base_type(typid);
}
GP_WRAP_END;
return false;
}
Oid
gpdb::GetOpclassFamily(Oid opclass)
{
GP_WRAP_START;
{
return get_opclass_family(opclass);
}
GP_WRAP_END;
return false;
}
List *
gpdb::LAppend(List *list, void *datum)
{
GP_WRAP_START;
{
return lappend(list, datum);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::LAppendInt(List *list, int iDatum)
{
GP_WRAP_START;
{
return lappend_int(list, iDatum);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::LAppendOid(List *list, Oid datum)
{
GP_WRAP_START;
{
return lappend_oid(list, datum);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::LPrepend(void *datum, List *list)
{
GP_WRAP_START;
{
return lcons(datum, list);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::LPrependInt(int datum, List *list)
{
GP_WRAP_START;
{
return lcons_int(datum, list);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::LPrependOid(Oid datum, List *list)
{
GP_WRAP_START;
{
return lcons_oid(datum, list);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::ListConcat(List *list1, List *list2)
{
GP_WRAP_START;
{
return list_concat(list1, list2);
}
GP_WRAP_END;
return NIL;
}
List *
gpdb::ListCopy(List *list)
{
GP_WRAP_START;
{
return list_copy(list);
}
GP_WRAP_END;
return NIL;
}
ListCell *
gpdb::ListHead(List *l)
{
GP_WRAP_START;
{
return list_head(l);
}
GP_WRAP_END;
return nullptr;
}
ListCell *
gpdb::ListTail(List *l)
{
GP_WRAP_START;
{
return list_tail(l);
}
GP_WRAP_END;
return nullptr;
}
uint32
gpdb::ListLength(List *l)
{
GP_WRAP_START;
{
return list_length(l);
}
GP_WRAP_END;
return 0;
}
void *
gpdb::ListNth(List *list, int n)
{
GP_WRAP_START;
{
return list_nth(list, n);
}
GP_WRAP_END;
return nullptr;
}
int
gpdb::ListNthInt(List *list, int n)
{
GP_WRAP_START;
{
return list_nth_int(list, n);
}
GP_WRAP_END;
return 0;
}
Oid
gpdb::ListNthOid(List *list, int n)
{
GP_WRAP_START;
{
return list_nth_oid(list, n);
}
GP_WRAP_END;
return 0;
}
bool
gpdb::ListMemberOid(List *list, Oid oid)
{
GP_WRAP_START;
{
return list_member_oid(list, oid);
}
GP_WRAP_END;
return false;
}
void
gpdb::ListFree(List *list)
{
GP_WRAP_START;
{
list_free(list);
return;
}
GP_WRAP_END;
}
void
gpdb::ListFreeDeep(List *list)
{
GP_WRAP_START;
{
list_free_deep(list);
return;
}
GP_WRAP_END;
}
TypeCacheEntry *
gpdb::LookupTypeCache(Oid type_id, int flags)
{
GP_WRAP_START;
{
/* catalog tables: pg_type, pg_operator, pg_opclass, pg_opfamily, pg_amop */
return lookup_type_cache(type_id, flags);
}
GP_WRAP_END;
return nullptr;
}
Value *
gpdb::MakeStringValue(char *str)
{
GP_WRAP_START;
{
return makeString(str);
}
GP_WRAP_END;
return nullptr;
}
Value *
gpdb::MakeIntegerValue(long i)
{
GP_WRAP_START;
{
return makeInteger(i);
}
GP_WRAP_END;
return nullptr;
}
Node *
gpdb::MakeIntConst(int32 intValue)
{
GP_WRAP_START;
{
return (Node *) makeConst(INT4OID, -1, InvalidOid, sizeof(int32),
Int32GetDatum(intValue), false, true);
}
GP_WRAP_END;
}
Node *
gpdb::MakeBoolConst(bool value, bool isnull)
{
GP_WRAP_START;
{
return makeBoolConst(value, isnull);
}
GP_WRAP_END;
return nullptr;
}
Node *
gpdb::MakeNULLConst(Oid type_oid)
{
GP_WRAP_START;
{
return (Node *) makeNullConst(type_oid, -1 /*consttypmod*/, InvalidOid);
}
GP_WRAP_END;
return nullptr;
}
Node *
gpdb::MakeSegmentFilterExpr(int segid)
{
GP_WRAP_START;
{
return (Node *) makeSegmentFilterExpr(segid);
}
GP_WRAP_END;
}
TargetEntry *
gpdb::MakeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
{
GP_WRAP_START;
{
return makeTargetEntry(expr, resno, resname, resjunk);
}
GP_WRAP_END;
return nullptr;
}
Var *
gpdb::MakeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod,
Index varlevelsup)
{
GP_WRAP_START;
{
// GPDB_91_MERGE_FIXME: collation
Oid collation = TypeCollation(vartype);
return makeVar(varno, varattno, vartype, vartypmod, collation,
varlevelsup);
}
GP_WRAP_END;
return nullptr;
}
void *
gpdb::MemCtxtAllocZeroAligned(MemoryContext context, Size size)
{
GP_WRAP_START;
{
return MemoryContextAllocZeroAligned(context, size);
}
GP_WRAP_END;
return nullptr;
}
void *
gpdb::MemCtxtAllocZero(MemoryContext context, Size size)
{
GP_WRAP_START;
{
return MemoryContextAllocZero(context, size);
}
GP_WRAP_END;
return nullptr;
}
void *
gpdb::MemCtxtRealloc(void *pointer, Size size)
{
GP_WRAP_START;
{
return repalloc(pointer, size);
}
GP_WRAP_END;
return nullptr;
}
char *
gpdb::MemCtxtStrdup(MemoryContext context, const char *string)
{
GP_WRAP_START;
{
return MemoryContextStrdup(context, string);
}
GP_WRAP_END;
return nullptr;
}
// Helper function to throw an error with errcode, message and hint, like you
// would with ereport(...) in the backend. This could be extended for other
// fields, but this is all we need at the moment.
void
gpdb::GpdbEreportImpl(int xerrcode, int severitylevel, const char *xerrmsg,
const char *xerrhint, const char *filename, int lineno,
const char *funcname)
{
GP_WRAP_START;
{
// We cannot use the ereport() macro here, because we want to pass on
// the caller's filename and line number. This is essentially an
// expanded version of ereport(). It will be caught by the
// GP_WRAP_END, and propagated up as a C++ exception, to be
// re-thrown as a Postgres error once we leave the C++ land.
if (errstart(severitylevel, TEXTDOMAIN))
{
errcode(xerrcode);
errmsg("%s", xerrmsg);
if (xerrhint)
{
errhint("%s", xerrhint);
}
errfinish(filename, lineno, funcname);
}
}
GP_WRAP_END;
}
char *
gpdb::NodeToString(void *obj)
{
GP_WRAP_START;
{
return nodeToString(obj);
}
GP_WRAP_END;
return nullptr;
}
Node *
gpdb::GetTypeDefault(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type */
return get_typdefault(typid);
}
GP_WRAP_END;
return nullptr;
}
double
gpdb::NumericToDoubleNoOverflow(Numeric num)
{
GP_WRAP_START;
{
return numeric_to_double_no_overflow(num);
}
GP_WRAP_END;
return 0.0;
}
bool
gpdb::NumericIsNan(Numeric num)
{
GP_WRAP_START;
{
return NUMERIC_IS_NAN(num);
}
GP_WRAP_END;
return false;
}
double
gpdb::ConvertTimeValueToScalar(Datum datum, Oid typid)
{
bool failure = false;
GP_WRAP_START;
{
return convert_timevalue_to_scalar(datum, typid, &failure);
}
GP_WRAP_END;
return 0.0;
}
double
gpdb::ConvertNetworkToScalar(Datum datum, Oid typid)
{
bool failure = false;
GP_WRAP_START;
{
return convert_network_to_scalar(datum, typid, &failure);
}
GP_WRAP_END;
return 0.0;
}
bool
gpdb::IsOpHashJoinable(Oid opno, Oid inputtype)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator */
if (!op_hashjoinable(opno, inputtype))
return false;
/*
* Even if oprcanhash is true, we need to verify that hash functions
* actually exist for this operator. This is because oprcanhash can be
* set to true while the operator is only registered in a btree opfamily
* and not in a hash opfamily, which would cause execution-time errors
* when trying to build hash tables.
*
* See get_op_hash_functions() in lsyscache.c which requires operators
* to be registered in a hash opfamily (amopmethod == HASH_AM_OID).
*/
RegProcedure hash_proc;
if (!get_op_hash_functions(opno, &hash_proc, NULL))
return false;
return true;
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsOpMergeJoinable(Oid opno, Oid inputtype)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator */
return op_mergejoinable(opno, inputtype);
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsOpStrict(Oid opno)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator, pg_proc */
return op_strict(opno);
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsOpNDVPreserving(Oid opno)
{
switch (opno)
{
// operators are NDV-preserving if the operation does not change the number
// of NDVs when one argument is a constant.
// note that we do additional checks later, e.g. col || 'const' is
// NDV-preserving, while col1 || col2 is not, same with arithmatic
// operators
case OIDTextConcatenateOperator:
case Int4AddOperator:
case Int8AddOperator:
case DateIntervalAddOperator:
case DateInt4AddOperator:
case DateTimeAddOperator:
case DateTimetzAddOperator:
case NumericAddOperator:
case TimestampIntervalAddOperator:
case IntervalTimestampAddOperator:
case Int4DateAddOperator:
return true;
default:
return false;
}
}
void
gpdb::GetOpInputTypes(Oid opno, Oid *lefttype, Oid *righttype)
{
GP_WRAP_START;
{
/* catalog tables: pg_operator */
op_input_types(opno, lefttype, righttype);
return;
}
GP_WRAP_END;
}
void *
gpdb::GPDBAlloc(Size size)
{
GP_WRAP_START;
{
return palloc(size);
}
GP_WRAP_END;
return nullptr;
}
void
gpdb::GPDBFree(void *ptr)
{
GP_WRAP_START;
{
pfree(ptr);
return;
}
GP_WRAP_END;
}
bool
gpdb::WalkQueryOrExpressionTree(Node *node, bool (*walker)(Node *, void *), void *context,
int flags)
{
GP_WRAP_START;
{
return query_or_expression_tree_walker_wrapper(node, walker, context, flags);
}
GP_WRAP_END;
return false;
}
Node *
gpdb::MutateQueryOrExpressionTree(Node *node, Node *(*mutator)(Node *, void *), void *context,
int flags)
{
GP_WRAP_START;
{
return query_or_expression_tree_mutator_wrapper(node, mutator, context, flags);
}
GP_WRAP_END;
return nullptr;
}
Query *
gpdb::MutateQueryTree(Query *query, Node *(*mutator)(Node *, void *), void *context,
int flags)
{
GP_WRAP_START;
{
return query_tree_mutator_wrapper(query, mutator, context, flags);
}
GP_WRAP_END;
return nullptr;
}
bool
gpdb::WalkQueryTree(Query *query, bool (*walker)(Node *, void *), void *context,
int flags)
{
GP_WRAP_START;
{
return query_tree_walker_wrapper(query, walker, context, flags);
}
GP_WRAP_END;
return false;
}
bool
gpdb::HasSubclassSlow(Oid rel_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_inherits */
return has_subclass_slow(rel_oid);
}
GP_WRAP_END;
return false;
}
GpPolicy *
gpdb::GetDistributionPolicy(Relation rel)
{
GP_WRAP_START;
{
// external tables are a special case, and we need to manually build
// the GpPolicy struct which contains the external table's distribution
if (rel_is_external_table(rel->rd_id))
{
return GpPolicyFetch(rel->rd_id);
}
// we determine the distribution at a later point for foreign tables
else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
{
return nullptr;
}
/* catalog tables: pg_class */
return relation_policy(rel);
}
GP_WRAP_END;
return nullptr;
}
gpos::BOOL
gpdb::IsChildPartDistributionMismatched(Relation rel)
{
GP_WRAP_START;
{
/* catalog tables: pg_class, pg_inherits */
return child_distribution_mismatch(rel);
}
GP_WRAP_END;
return false;
}
double
gpdb::CdbEstimatePartitionedNumTuples(Relation rel)
{
GP_WRAP_START;
{
return cdb_estimate_partitioned_numtuples(rel);
}
GP_WRAP_END;
}
PageEstimate
gpdb::CdbEstimatePartitionedNumPages(Relation rel)
{
GP_WRAP_START;
{
return cdb_estimate_partitioned_numpages(rel);
}
GP_WRAP_END;
}
void
gpdb::CloseRelation(Relation rel)
{
GP_WRAP_START;
{
RelationClose(rel);
return;
}
GP_WRAP_END;
}
List *
gpdb::GetRelationIndexes(Relation relation)
{
GP_WRAP_START;
{
if (relation->rd_rel->relhasindex)
{
/* catalog tables: from relcache */
return RelationGetIndexList(relation);
}
}
GP_WRAP_END;
return NIL;
}
MVNDistinct *
gpdb::GetMVNDistinct(Oid stat_oid)
{
GP_WRAP_START;
{
return statext_ndistinct_load(stat_oid);
}
GP_WRAP_END;
}
MVDependencies *
gpdb::GetMVDependencies(Oid stat_oid)
{
GP_WRAP_START;
{
return statext_dependencies_load(stat_oid, true);
}
GP_WRAP_END;
}
gpdb::RelationWrapper
gpdb::GetRelation(Oid rel_oid)
{
GP_WRAP_START;
{
/* catalog tables: relcache */
return RelationWrapper{RelationIdGetRelation(rel_oid)};
}
GP_WRAP_END;
}
ForeignScan *
gpdb::CreateForeignScan(Oid rel_oid, Index scanrelid, List *qual,
List *targetlist, Query *query, RangeTblEntry *rte)
{
GP_WRAP_START;
{
return BuildForeignScan(rel_oid, scanrelid, qual, targetlist, query,
rte);
}
GP_WRAP_END;
return nullptr;
}
TargetEntry *
gpdb::FindFirstMatchingMemberInTargetList(Node *node, List *targetlist)
{
GP_WRAP_START;
{
return tlist_member((Expr *) node, targetlist);
}
GP_WRAP_END;
return nullptr;
}
List *
gpdb::FindMatchingMembersInTargetList(Node *node, List *targetlist)
{
GP_WRAP_START;
{
return tlist_members(node, targetlist);
}
GP_WRAP_END;
return NIL;
}
bool
gpdb::Equals(void *p1, void *p2)
{
GP_WRAP_START;
{
return equal(p1, p2);
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsCompositeType(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type */
return type_is_rowtype(typid);
}
GP_WRAP_END;
return false;
}
bool
gpdb::IsTextRelatedType(Oid typid)
{
GP_WRAP_START;
{
/* catalog tables: pg_type */
char typcategory;
bool typispreferred;
get_type_category_preferred(typid, &typcategory, &typispreferred);
return typcategory == TYPCATEGORY_STRING;
}
GP_WRAP_END;
return false;
}
StringInfo
gpdb::MakeStringInfo(void)
{
GP_WRAP_START;
{
return makeStringInfo();
}
GP_WRAP_END;
return nullptr;
}
void
gpdb::AppendStringInfo(StringInfo str, const char *str1, const char *str2)
{
GP_WRAP_START;
{
appendStringInfo(str, "%s%s", str1, str2);
return;
}
GP_WRAP_END;
}
int
gpdb::FindNodes(Node *node, List *nodeTags)
{
GP_WRAP_START;
{
return find_nodes(node, nodeTags);
}
GP_WRAP_END;
return -1;
}
int
gpdb::CheckCollation(Node *node)
{
GP_WRAP_START;
{
return check_collation(node);
}
GP_WRAP_END;
return -1;
}
Node *
gpdb::CoerceToCommonType(ParseState *pstate, Node *node, Oid target_type,
const char *context)
{
GP_WRAP_START;
{
/* catalog tables: pg_type, pg_cast */
return coerce_to_common_type(pstate, node, target_type, context);
}
GP_WRAP_END;
return nullptr;
}
bool
gpdb::ResolvePolymorphicArgType(int numargs, Oid *argtypes, char *argmodes,
FuncExpr *call_expr)
{
GP_WRAP_START;
{
/* catalog tables: pg_proc */
return resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
(Node *) call_expr);
}
GP_WRAP_END;
return false;
}
// hash a list of const values with GPDB's hash function
int32
gpdb::CdbHashConstList(List *constants, int num_segments, Oid *hashfuncs)
{
GP_WRAP_START;
{
return cdbhash_const_list(constants, num_segments, hashfuncs);
}
GP_WRAP_END;
return 0;
}
unsigned int
gpdb::CdbHashRandomSeg(int num_segments)
{
GP_WRAP_START;
{
return cdbhashrandomseg(num_segments);
}
GP_WRAP_END;
return 0;
}
// check permissions on range table
void
gpdb::CheckRTPermissions(List *rtable)
{
GP_WRAP_START;
{
ExecCheckRTPerms(rtable, true);
return;
}
GP_WRAP_END;
}
// check that a table doesn't have UPDATE triggers.
bool
gpdb::HasUpdateTriggers(Oid relid)
{
GP_WRAP_START;
{
return has_update_triggers(relid, true);
}
GP_WRAP_END;
return false;
}
// get index op family properties
void
gpdb::IndexOpProperties(Oid opno, Oid opfamily, StrategyNumber *strategynumber,
Oid *righttype)
{
GP_WRAP_START;
{
/* catalog tables: pg_amop */
// Only the right type is returned to the caller, the left
// type is simply ignored.
Oid lefttype;
INT strategy;
get_op_opfamily_properties(opno, opfamily, false, &strategy, &lefttype,
righttype);
// Ensure the value of strategy doesn't get truncated when converted to StrategyNumber
GPOS_ASSERT(strategy >= 0 &&
strategy <= std::numeric_limits<StrategyNumber>::max());
*strategynumber = static_cast<StrategyNumber>(strategy);
return;
}
GP_WRAP_END;
}
// check whether index column is returnable (for index-only scans)
gpos::BOOL
gpdb::IndexCanReturn(Relation index, int attno)
{
GP_WRAP_START;
{
return index_can_return(index, attno);
}
GP_WRAP_END;
}
// get oids of opfamilies for the index keys
List *
gpdb::GetIndexOpFamilies(Oid index_oid)
{
GP_WRAP_START;
{
/* catalog tables: pg_index */
// We return the operator families of the index keys.
return get_index_opfamilies(index_oid);
}
GP_WRAP_END;
return NIL;
}
// get oids of families this operator belongs to
List *
gpdb::GetOpFamiliesForScOp(Oid opno)
{
GP_WRAP_START;
{
/* catalog tables: pg_amop */
// We return the operator families this operator
// belongs to.
return get_operator_opfamilies(opno);
}
GP_WRAP_END;
return NIL;
}
// get the OID of hash equality operator(s) compatible with the given op
Oid
gpdb::GetCompatibleHashOpFamily(Oid opno)
{
GP_WRAP_START;
{
return get_compatible_hash_opfamily(opno);
}
GP_WRAP_END;
return InvalidOid;
}
// get the OID of hash equality operator(s) compatible with the given op
Oid
gpdb::GetCompatibleLegacyHashOpFamily(Oid opno)
{
GP_WRAP_START;
{
return get_compatible_legacy_hash_opfamily(opno);
}
GP_WRAP_END;
return InvalidOid;
}
List *
gpdb::GetMergeJoinOpFamilies(Oid opno)
{
GP_WRAP_START;
{
/* catalog tables: pg_amop */
return get_mergejoin_opfamilies(opno);
}
GP_WRAP_END;
return NIL;
}
// get the OID of base elementtype for a given typid
// eg.: CREATE DOMAIN text_domain as text;
// SELECT oid, typbasetype from pg_type where typname = 'text_domain';
// oid | XXXXX --> Oid for text_domain
// typbasetype | 25 --> Oid for base element ie, TEXT
Oid
gpdb::GetBaseType(Oid typid)
{
GP_WRAP_START;
{
return getBaseType(typid);
}
GP_WRAP_END;
return InvalidOid;
}
// Evaluates 'expr' and returns the result as an Expr.
// Caller keeps ownership of 'expr' and takes ownership of the result
Expr *
gpdb::EvaluateExpr(Expr *expr, Oid result_type, int32 typmod)
{
GP_WRAP_START;
{
// GPDB_91_MERGE_FIXME: collation
return evaluate_expr(expr, result_type, typmod, InvalidOid);
}
GP_WRAP_END;
return nullptr;
}
char *
gpdb::DefGetString(DefElem *defelem)
{
GP_WRAP_START;
{
return defGetString(defelem);
}
GP_WRAP_END;
return nullptr;
}
Expr *
gpdb::TransformArrayConstToArrayExpr(Const *c)
{
GP_WRAP_START;
{
return transform_array_Const_to_ArrayExpr(c);
}
GP_WRAP_END;
return nullptr;
}
Node *
gpdb::EvalConstExpressions(Node *node)
{
GP_WRAP_START;
{
return eval_const_expressions(nullptr, node);
}
GP_WRAP_END;
return nullptr;
}
#ifdef FAULT_INJECTOR
FaultInjectorType_e
gpdb::InjectFaultInOptTasks(const char *fault_name)
{
GP_WRAP_START;
{
return FaultInjector_InjectFaultIfSet(fault_name, DDLNotSpecified, "",
"");
}
GP_WRAP_END;
return FaultInjectorTypeNotSpecified;
}
#endif
/*
* To detect changes to catalog tables that require resetting the Metadata
* Cache, we use the normal PostgreSQL catalog cache invalidation mechanism.
* We register a callback to a cache on all the catalog tables that contain
* information that's contained in the ORCA metadata cache.
* There is no fine-grained mechanism in the metadata cache for invalidating
* individual entries ATM, so we just blow the whole cache whenever anything
* changes. The callback simply increments a counter. Whenever we start
* planning a query, we check the counter to see if it has changed since the
* last planned query, and reset the whole cache if it has.
*
* To make sure we've covered all catalog tables that contain information
* that's stored in the metadata cache, there are "catalog tables: xxx"
* comments in all the calls to backend functions in this file. They indicate
* which catalog tables each function uses. We conservatively assume that
* anything fetched via the wrapper functions in this file can end up in the
* metadata cache and hence need to have an invalidation callback registered.
*/
static bool mdcache_invalidation_counter_registered = false;
static int64 mdcache_invalidation_counter = 0;
static int64 last_mdcache_invalidation_counter = 0;
static void
mdsyscache_invalidation_counter_callback(Datum /*arg*/, int /*cacheid*/,
uint32 /*hashvalue*/)
{
mdcache_invalidation_counter++;
}
static void
mdrelcache_invalidation_counter_callback(Datum /*arg*/, Oid /*relid*/)
{
mdcache_invalidation_counter++;
}
static void
register_mdcache_invalidation_callbacks(void)
{
/* These are all the catalog tables that we care about. */
int metadata_caches[] = {
AGGFNOID, /* pg_aggregate */
AMOPOPID, /* pg_amop */
CASTSOURCETARGET, /* pg_cast */
CONSTROID, /* pg_constraint */
OPEROID, /* pg_operator */
OPFAMILYOID, /* pg_opfamily */
STATRELATTINH, /* pg_statistics */
TYPEOID, /* pg_type */
PROCOID, /* pg_proc */
/*
* lookup_type_cache() will also access pg_opclass, via GetDefaultOpClass(),
* but there is no syscache for it. Postgres doesn't seem to worry about
* invalidating the type cache on updates to pg_opclass, so we don't
* worry about that either.
*/
/* pg_opclass */
/*
* Information from the following catalogs are included in the
* relcache, and any updates will generate relcache invalidation
* event. We'll catch the relcache invalidation event and don't need
* to register a catcache callback for them.
*/
/* pg_class */
/* pg_index */
/*
* pg_foreign_table is updated when a new external table is dropped/created,
* which will trigger a relcache invalidation event.
*/
/* pg_foreign_table */
/*
* XXX: no syscache on pg_inherits. Is that OK? For any partitioning
* changes, I think there will also be updates on pg_partition and/or
* pg_partition_rules.
*/
/* pg_inherits */
/*
* We assume that gp_segment_config will not change on the fly in a way that
* would affect ORCA
*/
/* gp_segment_config */
};
unsigned int i;
for (i = 0; i < lengthof(metadata_caches); i++)
{
CacheRegisterSyscacheCallback(metadata_caches[i],
&mdsyscache_invalidation_counter_callback,
(Datum) 0);
}
/* also register the relcache callback */
CacheRegisterRelcacheCallback(&mdrelcache_invalidation_counter_callback,
(Datum) 0);
}
// Has there been any catalog changes since last call?
bool
gpdb::MDCacheNeedsReset(void)
{
GP_WRAP_START;
{
if (!mdcache_invalidation_counter_registered)
{
register_mdcache_invalidation_callbacks();
mdcache_invalidation_counter_registered = true;
}
if (last_mdcache_invalidation_counter == mdcache_invalidation_counter)
{
return false;
}
else
{
last_mdcache_invalidation_counter = mdcache_invalidation_counter;
return true;
}
}
GP_WRAP_END;
return true;
}
// returns true if a query cancel is requested in GPDB
bool
gpdb::IsAbortRequested(void)
{
// No GP_WRAP_START/END needed here. We just check these global flags,
// it cannot throw an ereport().
return (QueryCancelPending || ProcDiePending);
}
// Given the type OID, get the typelem (InvalidOid if not an array type).
Oid
gpdb::GetElementType(Oid array_type_oid)
{
GP_WRAP_START;
{
return get_element_type(array_type_oid);
}
GP_WRAP_END;
}
GpPolicy *
gpdb::MakeGpPolicy(GpPolicyType ptype, int nattrs, int numsegments)
{
GP_WRAP_START;
{
/*
* FIXME_TABLE_EXPAND: it used by ORCA, help...
*/
return makeGpPolicy(ptype, nattrs, numsegments);
}
GP_WRAP_END;
}
uint32
gpdb::HashChar(Datum d)
{
GP_WRAP_START;
{
return DatumGetUInt32(DirectFunctionCall1(hashchar, d));
}
GP_WRAP_END;
}
uint32
gpdb::HashBpChar(Datum d)
{
GP_WRAP_START;
{
return DatumGetUInt32(
DirectFunctionCall1Coll(hashbpchar, C_COLLATION_OID, d));
}
GP_WRAP_END;
}
uint32
gpdb::HashText(Datum d)
{
GP_WRAP_START;
{
return DatumGetUInt32(
DirectFunctionCall1Coll(hashtext, C_COLLATION_OID, d));
}
GP_WRAP_END;
}
uint32
gpdb::HashName(Datum d)
{
GP_WRAP_START;
{
return DatumGetUInt32(DirectFunctionCall1(hashname, d));
}
GP_WRAP_END;
}
uint32
gpdb::UUIDHash(Datum d)
{
GP_WRAP_START;
{
return DatumGetUInt32(DirectFunctionCall1(uuid_hash, d));
}
GP_WRAP_END;
}
void *
gpdb::GPDBMemoryContextAlloc(MemoryContext context, Size size)
{
GP_WRAP_START;
{
return MemoryContextAlloc(context, size);
}
GP_WRAP_END;
return nullptr;
}
void
gpdb::GPDBMemoryContextDelete(MemoryContext context)
{
GP_WRAP_START;
{
MemoryContextDelete(context);
}
GP_WRAP_END;
}
MemoryContext
gpdb::GPDBAllocSetContextCreate()
{
GP_WRAP_START;
{
MemoryContext cxt;
cxt =
AllocSetContextCreate(OptimizerMemoryContext, "GPORCA memory pool",
ALLOCSET_DEFAULT_SIZES);
/*
* Declare it as accounting root so that we can call
* MemoryContextGetCurrentSpace() on it.
*/
MemoryContextDeclareAccountingRoot(cxt);
return cxt;
}
GP_WRAP_END;
return nullptr;
}
bool
gpdb::ExpressionReturnsSet(Node *clause)
{
GP_WRAP_START;
{
return expression_returns_set(clause);
}
GP_WRAP_END;
}
List *
gpdb::GetRelChildIndexes(Oid reloid)
{
List *partoids = NIL;
GP_WRAP_START;
{
if (InvalidOid == reloid)
{
return NIL;
}
partoids = find_inheritance_children(reloid, NoLock);
}
GP_WRAP_END;
return partoids;
}
Oid
gpdb::GetForeignServerId(Oid reloid)
{
GP_WRAP_START;
{
return GetForeignServerIdByRelId(reloid);
}
GP_WRAP_END;
return 0;
}
// Locks on partition leafs and indexes are held during optimizer (after
// parse-analyze stage). ORCA need this function to lock relation. Here
// we do not need to consider lock-upgrade issue, reasons are:
// 1. Only UPDATE|DELETE statement may upgrade lock level
// 2. ORCA currently does not support DML on partition tables
// 3. If not partition table, then parser should have already locked
// 4. Even later ORCA support DML on partition tables, the lock mode
// of leafs should be the same as the mode in root's RTE's rellockmode
// 5. Index does not have lock-upgrade problem.
void
gpdb::GPDBLockRelationOid(Oid reloid, LOCKMODE lockmode)
{
GP_WRAP_START;
{
LockRelationOid(reloid, lockmode);
}
GP_WRAP_END;
}
char *
gpdb::GetRelFdwName(Oid reloid)
{
GP_WRAP_START;
{
Oid fs_id = GetForeignServerIdByRelId(reloid);
ForeignServer *fs = GetForeignServer(fs_id);
ForeignDataWrapper *fdw = GetForeignDataWrapper(fs->fdwid);
return fdw->fdwname;
}
GP_WRAP_END;
return nullptr;
}
PathTarget *
gpdb::MakePathtargetFromTlist(List *tlist)
{
GP_WRAP_START;
{
return make_pathtarget_from_tlist(tlist);
}
GP_WRAP_END;
}
void
gpdb::SplitPathtargetAtSrfs(PlannerInfo *root, PathTarget *target,
PathTarget *input_target, List **targets,
List **targets_contain_srfs)
{
GP_WRAP_START;
{
split_pathtarget_at_srfs(root, target, input_target, targets,
targets_contain_srfs);
}
GP_WRAP_END;
}
List *
gpdb::MakeTlistFromPathtarget(PathTarget *target)
{
GP_WRAP_START;
{
return make_tlist_from_pathtarget(target);
}
GP_WRAP_END;
return NIL;
}
Node *
gpdb::Expression_tree_mutator(Node *node, Node *(*mutator)(Node*, void*), void *context)
{
GP_WRAP_START;
{
return expression_tree_mutator_wrapper(node, mutator, context);
}
GP_WRAP_END;
return nullptr;
}
TargetEntry *
gpdb::TlistMember(Expr *node, List *targetlist)
{
GP_WRAP_START;
{
return tlist_member(node, targetlist);
}
GP_WRAP_END;
return nullptr;
}
Var *
gpdb::MakeVarFromTargetEntry(Index varno, TargetEntry *tle)
{
GP_WRAP_START;
{
return makeVarFromTargetEntry(varno, tle);
}
GP_WRAP_END;
}
TargetEntry *
gpdb::FlatCopyTargetEntry(TargetEntry *src_tle)
{
GP_WRAP_START;
{
return flatCopyTargetEntry(src_tle);
}
GP_WRAP_END;
}
// Returns true if type is a RANGE
// pg_type (typtype = 'r')
bool
gpdb::IsTypeRange(Oid typid)
{
GP_WRAP_START;
{
return type_is_range(typid);
}
GP_WRAP_END;
return false;
}
char *
gpdb::GetRelAmName(Oid reloid)
{
GP_WRAP_START;
{
return GetAmName(reloid);
}
GP_WRAP_END;
return nullptr;
}
// Get IndexAmRoutine struct for the given access method handler.
IndexAmRoutine *
gpdb::GetIndexAmRoutineFromAmHandler(Oid am_handler)
{
GP_WRAP_START;
{
return GetIndexAmRoutine(am_handler);
}
GP_WRAP_END;
}
bool
gpdb::TestexprIsHashable(Node *testexpr, List *param_ids)
{
GP_WRAP_START;
{
return testexpr_is_hashable(testexpr, param_ids);
}
GP_WRAP_END;
return false;
}
// EOF