openGauss-server/src/common/backend/utils/cache/lsyscache.cpp

4720 lines
151 KiB
C++

/* -------------------------------------------------------------------------
*
* lsyscache.c
* Convenience routines for common queries in the system catalog cache.
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/utils/cache/lsyscache.c
*
* NOTES
* Eventually, the index information should go through here, too.
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "access/hash.h"
#include "access/nbtree.h"
#include "access/transam.h"
#include "bootstrap/bootstrap.h"
#include "catalog/gs_obsscaninfo.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_obsscaninfo.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_partition_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_range.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_synonym.h"
#include "catalog/pg_type.h"
#include "catalog/pg_ts_config.h"
#ifdef PGXC
#include "catalog/pgxc_class.h"
#include "catalog/pgxc_node.h"
#include "catalog/pgxc_group.h"
#include "catalog/pg_resource_pool.h"
#include "catalog/pg_workload_group.h"
#include "catalog/pg_app_workloadgroup_mapping.h"
#include "catalog/namespace.h"
#endif
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/planner.h"
#include "storage/lmgr.h"
#include "storage/sinval.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "storage/proc.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/typcache.h"
#include "optimizer/streamplan.h"
#include "pgxc/pgxc.h"
#include "utils/acl.h"
#include "catalog/pgxc_group.h"
#ifdef ENABLE_MULTIPLE_NODES
/* PGXC Cache APIs */
extern Oid GetCachedGroupid(Oid relid);
extern void CacheGroupId(Oid relid, Oid groupid);
extern uint64 GetPGXCGlobalVersion();
extern Oid GetCachedGrpidByName(const char* name, uint64 version);
extern void UpdateGrpidInCache(const char* group_name, uint64 groupid, uint64 version);
#endif
/* ---------- AMOP CACHES ---------- */
/*
* op_in_opfamily
*
* Return t iff operator 'opno' is in operator family 'opfamily'.
*
* This function only considers search operators, not ordering operators.
*/
bool op_in_opfamily(Oid opno, Oid opfamily)
{
return SearchSysCacheExists3(
AMOPOPID, ObjectIdGetDatum(opno), CharGetDatum(AMOP_SEARCH), ObjectIdGetDatum(opfamily));
}
/*
* get_op_opfamily_strategy
*
* Get the operator's strategy number within the specified opfamily,
* or 0 if it's not a member of the opfamily.
*
* This function only considers search operators, not ordering operators.
*/
int get_op_opfamily_strategy(Oid opno, Oid opfamily)
{
HeapTuple tp;
Form_pg_amop amop_tup;
int result;
tp = SearchSysCache3(AMOPOPID, ObjectIdGetDatum(opno), CharGetDatum(AMOP_SEARCH), ObjectIdGetDatum(opfamily));
if (!HeapTupleIsValid(tp)) {
return 0;
}
amop_tup = (Form_pg_amop)GETSTRUCT(tp);
result = amop_tup->amopstrategy;
ReleaseSysCache(tp);
return result;
}
/*
* get_op_opfamily_sortfamily
*
* If the operator is an ordering operator within the specified opfamily,
* return its amopsortfamily OID; else return InvalidOid.
*/
Oid get_op_opfamily_sortfamily(Oid opno, Oid opfamily)
{
HeapTuple tp;
Form_pg_amop amop_tup;
Oid result;
tp = SearchSysCache3(AMOPOPID, ObjectIdGetDatum(opno), CharGetDatum(AMOP_ORDER), ObjectIdGetDatum(opfamily));
if (!HeapTupleIsValid(tp)) {
return InvalidOid;
}
amop_tup = (Form_pg_amop)GETSTRUCT(tp);
result = amop_tup->amopsortfamily;
ReleaseSysCache(tp);
return result;
}
/*
* get_op_opfamily_properties
*
* Get the operator's strategy number and declared input data types
* within the specified opfamily.
*
* Caller should already have verified that opno is a member of opfamily,
* therefore we raise an error if the tuple is not found.
*/
void get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op, int* strategy, Oid* lefttype, Oid* righttype)
{
HeapTuple tp;
Form_pg_amop amop_tup;
tp = SearchSysCache3(AMOPOPID,
ObjectIdGetDatum(opno),
CharGetDatum(ordering_op ? AMOP_ORDER : AMOP_SEARCH),
ObjectIdGetDatum(opfamily));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator %u is not a member of opfamily %u", opno, opfamily)));
}
amop_tup = (Form_pg_amop)GETSTRUCT(tp);
*strategy = amop_tup->amopstrategy;
*lefttype = amop_tup->amoplefttype;
*righttype = amop_tup->amoprighttype;
ReleaseSysCache(tp);
}
/*
* get_opfamily_member
* Get the OID of the operator that implements the specified strategy
* with the specified datatypes for the specified opfamily.
*
* Returns InvalidOid if there is no pg_amop entry for the given keys.
*/
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
{
HeapTuple tp;
Form_pg_amop amop_tup;
Oid result;
tp = SearchSysCache4(AMOPSTRATEGY,
ObjectIdGetDatum(opfamily),
ObjectIdGetDatum(lefttype),
ObjectIdGetDatum(righttype),
Int16GetDatum(strategy));
if (!HeapTupleIsValid(tp)) {
return InvalidOid;
}
amop_tup = (Form_pg_amop)GETSTRUCT(tp);
result = amop_tup->amopopr;
ReleaseSysCache(tp);
return result;
}
/*
* get_ordering_op_properties
* Given the OID of an ordering operator (a btree "<" or ">" operator),
* determine its opfamily, its declared input datatype, and its
* strategy number (BTLessStrategyNumber or BTGreaterStrategyNumber).
*
* Returns TRUE if successful, FALSE if no matching pg_amop entry exists.
* (This indicates that the operator is not a valid ordering operator.)
*
* Note: the operator could be registered in multiple families, for example
* if someone were to build a "reverse sort" opfamily. This would result in
* uncertainty as to whether "ORDER BY USING op" would default to NULLS FIRST
* or NULLS LAST, as well as inefficient planning due to failure to match up
* pathkeys that should be the same. So we want a determinate result here.
* Because of the way the syscache search works, we'll use the interpretation
* associated with the opfamily with smallest OID, which is probably
* determinate enough. Since there is no longer any particularly good reason
* to build reverse-sort opfamilies, it doesn't seem worth expending any
* additional effort on ensuring consistency.
*/
bool get_ordering_op_properties(Oid opno, Oid* opfamily, Oid* opcintype, int16* strategy)
{
bool result = false;
CatCList* catlist = NULL;
int i;
/* ensure outputs are initialized on failure */
*opfamily = InvalidOid;
*opcintype = InvalidOid;
*strategy = 0;
/*
* Search pg_amop to see if the target operator is registered as the "<"
* or ">" operator of any btree opfamily.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
/* must be btree */
if (aform->amopmethod != BTREE_AM_OID) {
continue;
}
if (aform->amopstrategy == BTLessStrategyNumber || aform->amopstrategy == BTGreaterStrategyNumber) {
/* Found it ... should have consistent input types */
if (aform->amoplefttype == aform->amoprighttype) {
/* Found a suitable opfamily, return info */
*opfamily = aform->amopfamily;
*opcintype = aform->amoplefttype;
*strategy = aform->amopstrategy;
result = true;
break;
}
}
}
ReleaseSysCacheList(catlist);
return result;
}
/*
* get_compare_function_for_ordering_op
* Get the OID of the datatype-specific btree comparison function
* associated with an ordering operator (a "<" or ">" operator).
*
* *cmpfunc receives the comparison function OID.
* *reverse is set FALSE if the operator is "<", TRUE if it's ">"
* (indicating the comparison result must be negated before use).
*
* Returns TRUE if successful, FALSE if no btree function can be found.
* (This indicates that the operator is not a valid ordering operator.)
*/
bool get_compare_function_for_ordering_op(Oid opno, Oid* cmpfunc, bool* reverse)
{
Oid opfamily;
Oid opcintype;
int16 strategy;
/* Find the operator in pg_amop */
if (get_ordering_op_properties(opno, &opfamily, &opcintype, &strategy)) {
/* Found a suitable opfamily, get matching support function */
*cmpfunc = get_opfamily_proc(opfamily, opcintype, opcintype, BTORDER_PROC);
if (!OidIsValid(*cmpfunc)) {
/* should not happen */
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("missing support function %d(%u,%u) in opfamily %u",
BTORDER_PROC,
opcintype,
opcintype,
opfamily)));
}
*reverse = (strategy == BTGreaterStrategyNumber);
return true;
}
/* ensure outputs are set on failure */
*cmpfunc = InvalidOid;
*reverse = false;
return false;
}
/*
* get_sort_function_for_ordering_op
* Get the OID of the datatype-specific btree sort support function,
* or if there is none, the btree comparison function,
* associated with an ordering operator (a "<" or ">" operator).
*
* *sortfunc receives the support or comparison function OID.
* *issupport is set TRUE if it's a support func, FALSE if a comparison func.
* *reverse is set FALSE if the operator is "<", TRUE if it's ">"
* (indicating that comparison results must be negated before use).
*
* Returns TRUE if successful, FALSE if no btree function can be found.
* (This indicates that the operator is not a valid ordering operator.)
*/
bool get_sort_function_for_ordering_op(Oid opno, Oid* sortfunc, bool* issupport, bool* reverse)
{
Oid opfamily;
Oid opcintype;
int16 strategy;
/* Find the operator in pg_amop */
if (get_ordering_op_properties(opno, &opfamily, &opcintype, &strategy)) {
/* Found a suitable opfamily, get matching support function */
*sortfunc = get_opfamily_proc(opfamily, opcintype, opcintype, BTSORTSUPPORT_PROC);
if (OidIsValid(*sortfunc)) {
*issupport = true;
} else {
/* opfamily doesn't provide sort support, get comparison func */
*sortfunc = get_opfamily_proc(opfamily, opcintype, opcintype, BTORDER_PROC);
if (!OidIsValid(*sortfunc)) {
/* should not happen */
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("missing support function %d(%u,%u) in opfamily %u",
BTORDER_PROC,
opcintype,
opcintype,
opfamily)));
}
*issupport = false;
}
*reverse = (strategy == BTGreaterStrategyNumber);
return true;
}
/* ensure outputs are set on failure */
*sortfunc = InvalidOid;
*issupport = false;
*reverse = false;
return false;
}
/*
* get_equality_op_for_ordering_op
* Get the OID of the datatype-specific btree equality operator
* associated with an ordering operator (a "<" or ">" operator).
*
* If "reverse" isn't NULL, also set *reverse to FALSE if the operator is "<",
* TRUE if it's ">"
*
* Returns InvalidOid if no matching equality operator can be found.
* (This indicates that the operator is not a valid ordering operator.)
*/
Oid get_equality_op_for_ordering_op(Oid opno, bool* reverse)
{
Oid result = InvalidOid;
Oid opfamily;
Oid opcintype;
int16 strategy;
/* Find the operator in pg_amop */
if (get_ordering_op_properties(opno, &opfamily, &opcintype, &strategy)) {
/* Found a suitable opfamily, get matching equality operator */
result = get_opfamily_member(opfamily, opcintype, opcintype, BTEqualStrategyNumber);
if (reverse != NULL) {
*reverse = (strategy == BTGreaterStrategyNumber);
}
}
return result;
}
/*
* get_ordering_op_for_equality_op
* Get the OID of a datatype-specific btree ordering operator
* associated with an equality operator. (If there are multiple
* possibilities, assume any one will do.)
*
* This function is used when we have to sort data before unique-ifying,
* and don't much care which sorting op is used as long as it's compatible
* with the intended equality operator. Since we need a sorting operator,
* it should be single-data-type even if the given operator is cross-type.
* The caller specifies whether to find an op for the LHS or RHS data type.
*
* Returns InvalidOid if no matching ordering operator can be found.
*/
Oid get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
{
Oid result = InvalidOid;
CatCList* catlist = NULL;
int i;
/*
* Search pg_amop to see if the target operator is registered as the "="
* operator of any btree opfamily.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
/* must be btree */
if (aform->amopmethod != BTREE_AM_OID) {
continue;
}
if (aform->amopstrategy == BTEqualStrategyNumber) {
/* Found a suitable opfamily, get matching ordering operator */
Oid typid;
typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
result = get_opfamily_member(aform->amopfamily, typid, typid, BTLessStrategyNumber);
if (OidIsValid(result)) {
break;
}
/* failure probably shouldn't happen, but keep looking if so */
}
}
ReleaseSysCacheList(catlist);
return result;
}
/*
* get_mergejoin_opfamilies
* Given a putatively mergejoinable operator, return a list of the OIDs
* of the btree opfamilies in which it represents equality.
*
* It is possible (though at present unusual) for an operator to be equality
* in more than one opfamily, hence the result is a list. This also lets us
* return NIL if the operator is not found in any opfamilies.
*
* The planner currently uses simple equal() tests to compare the lists
* returned by this function, which makes the list order relevant, though
* strictly speaking it should not be. Because of the way syscache list
* searches are handled, in normal operation the result will be sorted by OID
* so everything works fine. If running with system index usage disabled,
* the result ordering is unspecified and hence the planner might fail to
* recognize optimization opportunities ... but that's hardly a scenario in
* which performance is good anyway, so there's no point in expending code
* or cycles here to guarantee the ordering in that case.
*/
List* get_mergejoin_opfamilies(Oid opno)
{
List* result = NIL;
CatCList* catlist = NULL;
int i;
/*
* Search pg_amop to see if the target operator is registered as the "="
* operator of any btree opfamily.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
/* must be btree equality */
if (aform->amopmethod == BTREE_AM_OID && aform->amopstrategy == BTEqualStrategyNumber) {
result = lappend_oid(result, aform->amopfamily);
}
}
ReleaseSysCacheList(catlist);
return result;
}
/*
* get_compatible_hash_operators
* Get the OID(s) of hash equality operator(s) compatible with the given
* operator, but operating on its LHS and/or RHS datatype.
*
* An operator for the LHS type is sought and returned into *lhs_opno if
* lhs_opno isn't NULL. Similarly, an operator for the RHS type is sought
* and returned into *rhs_opno if rhs_opno isn't NULL.
*
* If the given operator is not cross-type, the results should be the same
* operator, but in cross-type situations they will be different.
*
* Returns true if able to find the requested operator(s), false if not.
* (This indicates that the operator should not have been marked oprcanhash.)
*/
bool get_compatible_hash_operators(Oid opno, Oid* lhs_opno, Oid* rhs_opno)
{
bool result = false;
CatCList* catlist = NULL;
int i;
/* Ensure output args are initialized on failure */
if (lhs_opno != NULL) {
*lhs_opno = InvalidOid;
}
if (rhs_opno != NULL) {
*rhs_opno = InvalidOid;
}
/*
* Search pg_amop to see if the target operator is registered as the "="
* operator of any hash opfamily. If the operator is registered in
* multiple opfamilies, assume we can use any one.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
if (aform->amopmethod == HASH_AM_OID && aform->amopstrategy == HTEqualStrategyNumber) {
/* No extra lookup needed if given operator is single-type */
if (aform->amoplefttype == aform->amoprighttype) {
if (lhs_opno != NULL) {
*lhs_opno = opno;
}
if (rhs_opno != NULL) {
*rhs_opno = opno;
}
result = true;
break;
}
/*
* Get the matching single-type operator(s). Failure probably
* shouldn't happen --- it implies a bogus opfamily --- but
* continue looking if so.
*/
if (lhs_opno != NULL) {
*lhs_opno = get_opfamily_member(
aform->amopfamily, aform->amoplefttype, aform->amoplefttype, HTEqualStrategyNumber);
if (!OidIsValid(*lhs_opno)) {
continue;
}
/* Matching LHS found, done if caller doesn't want RHS */
if (rhs_opno == NULL) {
result = true;
break;
}
}
if (rhs_opno != NULL) {
*rhs_opno = get_opfamily_member(
aform->amopfamily, aform->amoprighttype, aform->amoprighttype, HTEqualStrategyNumber);
if (!OidIsValid(*rhs_opno)) {
/* Forget any LHS operator from this opfamily */
if (lhs_opno != NULL)
*lhs_opno = InvalidOid;
continue;
}
/* Matching RHS found, so done */
result = true;
break;
}
}
}
ReleaseSysCacheList(catlist);
return result;
}
/*
* get_op_hash_functions
* Get the OID(s) of hash support function(s) compatible with the given
* operator, operating on its LHS and/or RHS datatype as required.
*
* A function for the LHS type is sought and returned into *lhs_procno if
* lhs_procno isn't NULL. Similarly, a function for the RHS type is sought
* and returned into *rhs_procno if rhs_procno isn't NULL.
*
* If the given operator is not cross-type, the results should be the same
* function, but in cross-type situations they will be different.
*
* Returns true if able to find the requested function(s), false if not.
* (This indicates that the operator should not have been marked oprcanhash.)
*/
bool get_op_hash_functions(Oid opno, RegProcedure* lhs_procno, RegProcedure* rhs_procno)
{
bool result = false;
CatCList* catlist = NULL;
int i;
/* Ensure output args are initialized on failure */
if (lhs_procno != NULL) {
*lhs_procno = InvalidOid;
}
if (rhs_procno != NULL) {
*rhs_procno = InvalidOid;
}
/*
* Search pg_amop to see if the target operator is registered as the "="
* operator of any hash opfamily. If the operator is registered in
* multiple opfamilies, assume we can use any one.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
if (aform->amopmethod == HASH_AM_OID && aform->amopstrategy == HTEqualStrategyNumber) {
/*
* Get the matching support function(s). Failure probably
* shouldn't happen --- it implies a bogus opfamily --- but
* continue looking if so.
*/
if (lhs_procno != NULL) {
*lhs_procno = get_opfamily_proc(aform->amopfamily, aform->amoplefttype, aform->amoplefttype, HASHPROC);
if (!OidIsValid(*lhs_procno)) {
continue;
}
/* Matching LHS found, done if caller doesn't want RHS */
if (rhs_procno == NULL) {
result = true;
break;
}
/* Only one lookup needed if given operator is single-type */
if (aform->amoplefttype == aform->amoprighttype) {
*rhs_procno = *lhs_procno;
result = true;
break;
}
}
if (rhs_procno != NULL) {
*rhs_procno =
get_opfamily_proc(aform->amopfamily, aform->amoprighttype, aform->amoprighttype, HASHPROC);
if (!OidIsValid(*rhs_procno)) {
/* Forget any LHS function from this opfamily */
if (lhs_procno != NULL) {
*lhs_procno = InvalidOid;
}
continue;
}
/* Matching RHS found, so done */
result = true;
break;
}
}
}
ReleaseSysCacheList(catlist);
return result;
}
/*
* get_op_btree_interpretation
* Given an operator's OID, find out which btree opfamilies it belongs to,
* and what properties it has within each one. The results are returned
* as a palloc'd list of OpBtreeInterpretation structs.
*
* In addition to the normal btree operators, we consider a <> operator to be
* a "member" of an opfamily if its negator is an equality operator of the
* opfamily. ROWCOMPARE_NE is returned as the strategy number for this case.
*/
List* get_op_btree_interpretation(Oid opno)
{
List* result = NIL;
OpBtreeInterpretation* thisresult = NULL;
CatCList* catlist = NULL;
int i;
/*
* Find all the pg_amop entries containing the operator.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop)GETSTRUCT(op_tuple);
StrategyNumber op_strategy;
/* must be btree */
if (op_form->amopmethod != BTREE_AM_OID) {
continue;
}
/* Get the operator's btree strategy number */
op_strategy = (StrategyNumber)op_form->amopstrategy;
Assert(op_strategy >= 1 && op_strategy <= 5);
thisresult = (OpBtreeInterpretation*)palloc(sizeof(OpBtreeInterpretation));
thisresult->opfamily_id = op_form->amopfamily;
thisresult->strategy = op_strategy;
thisresult->oplefttype = op_form->amoplefttype;
thisresult->oprighttype = op_form->amoprighttype;
result = lappend(result, thisresult);
}
ReleaseSysCacheList(catlist);
/*
* If we didn't find any btree opfamily containing the operator, perhaps
* it is a <> operator. See if it has a negator that is in an opfamily.
*/
if (result == NIL) {
Oid op_negator = get_negator(opno);
if (OidIsValid(op_negator)) {
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(op_negator));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop)GETSTRUCT(op_tuple);
StrategyNumber op_strategy;
/* must be btree */
if (op_form->amopmethod != BTREE_AM_OID) {
continue;
}
/* Get the operator's btree strategy number */
op_strategy = (StrategyNumber)op_form->amopstrategy;
Assert(op_strategy >= 1 && op_strategy <= 5);
/* Only consider negators that are = */
if (op_strategy != BTEqualStrategyNumber) {
continue;
}
/* OK, report it with "strategy" ROWCOMPARE_NE */
thisresult = (OpBtreeInterpretation*)palloc(sizeof(OpBtreeInterpretation));
thisresult->opfamily_id = op_form->amopfamily;
thisresult->strategy = ROWCOMPARE_NE;
thisresult->oplefttype = op_form->amoplefttype;
thisresult->oprighttype = op_form->amoprighttype;
result = lappend(result, thisresult);
}
ReleaseSysCacheList(catlist);
}
}
return result;
}
/*
* equality_ops_are_compatible
* Return TRUE if the two given equality operators have compatible
* semantics.
*
* This is trivially true if they are the same operator. Otherwise,
* we look to see if they can be found in the same btree or hash opfamily.
* Either finding allows us to assume that they have compatible notions
* of equality. (The reason we need to do these pushups is that one might
* be a cross-type operator; for instance int24eq vs int4eq.)
*/
bool equality_ops_are_compatible(Oid opno1, Oid opno2)
{
bool result = false;
CatCList* catlist = NULL;
int i;
/* Easy if they're the same operator */
if (opno1 == opno2) {
return true;
}
/*
* We search through all the pg_amop entries for opno1.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno1));
result = false;
for (i = 0; i < catlist->n_members; i++) {
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop)GETSTRUCT(op_tuple);
/* must be btree or hash */
if (op_form->amopmethod == BTREE_AM_OID || op_form->amopmethod == HASH_AM_OID) {
if (op_in_opfamily(opno2, op_form->amopfamily)) {
result = true;
break;
}
}
}
ReleaseSysCacheList(catlist);
return result;
}
/*
* get_opfamily_proc
* Get the OID of the specified support function
* for the specified opfamily and datatypes.
*
* Returns InvalidOid if there is no pg_amproc entry for the given keys.
*/
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
{
HeapTuple tp;
Form_pg_amproc amproc_tup;
RegProcedure result;
tp = SearchSysCache4(AMPROCNUM,
ObjectIdGetDatum(opfamily),
ObjectIdGetDatum(lefttype),
ObjectIdGetDatum(righttype),
Int16GetDatum(procnum));
if (!HeapTupleIsValid(tp)) {
return InvalidOid;
}
amproc_tup = (Form_pg_amproc)GETSTRUCT(tp);
result = amproc_tup->amproc;
ReleaseSysCache(tp);
return result;
}
/*
* get_attname
* Given the relation id and the attribute number,
* return the "attname" field from the attribute relation.
*
* Note: returns a palloc'd copy of the string, or NULL if no such attribute.
*/
char* get_attname(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum));
if (HeapTupleIsValid(tp)) {
Form_pg_attribute att_tup = (Form_pg_attribute)GETSTRUCT(tp);
char* result = NULL;
result = pstrdup(NameStr(att_tup->attname));
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
/*
* get_relid_attribute_name
*
* Same as above routine get_attname(), except that error
* is handled by elog() instead of returning NULL.
*/
char* get_relid_attribute_name(Oid relid, AttrNumber attnum)
{
char* attname = NULL;
attname = get_attname(relid, attnum);
if (attname == NULL) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for attribute %d of relation %u", attnum, relid)));
}
return attname;
}
/*
* get_attnum
*
* Given the relation id and the attribute name,
* return the "attnum" field from the attribute relation.
*
* Returns InvalidAttrNumber if the attr doesn't exist (or is dropped).
*/
AttrNumber get_attnum(Oid relid, const char* attname)
{
HeapTuple tp;
tp = SearchSysCacheAttName(relid, attname);
if (HeapTupleIsValid(tp)) {
Form_pg_attribute att_tup = (Form_pg_attribute)GETSTRUCT(tp);
AttrNumber result;
result = att_tup->attnum;
ReleaseSysCache(tp);
return result;
} else {
return InvalidAttrNumber;
}
}
/*
* get_atttype
*
* Given the relation OID and the attribute number with the relation,
* return the attribute type OID.
*/
Oid get_atttype(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum));
if (HeapTupleIsValid(tp)) {
Form_pg_attribute att_tup = (Form_pg_attribute)GETSTRUCT(tp);
Oid result;
result = att_tup->atttypid;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_atttypmod
*
* Given the relation id and the attribute number,
* return the "atttypmod" field from the attribute relation.
*/
int32 get_atttypmod(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum));
if (HeapTupleIsValid(tp)) {
Form_pg_attribute att_tup = (Form_pg_attribute)GETSTRUCT(tp);
int32 result;
result = att_tup->atttypmod;
ReleaseSysCache(tp);
return result;
} else {
return -1;
}
}
/*
* get_atttypetypmodcoll
*
* A three-fer: given the relation id and the attribute number,
* fetch atttypid, atttypmod, and attcollation in a single cache lookup.
*
* Unlike the otherwise-similar get_atttype/get_atttypmod, this routine
* raises an error if it can't obtain the information.
*/
void get_atttypetypmodcoll(Oid relid, AttrNumber attnum, Oid* typid, int32* typmod, Oid* collid)
{
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for attribute %d of relation %u", attnum, relid)));
}
att_tup = (Form_pg_attribute)GETSTRUCT(tp);
*typid = att_tup->atttypid;
*typmod = att_tup->atttypmod;
*collid = att_tup->attcollation;
ReleaseSysCache(tp);
}
/*
* get_collation_name
* Returns the name of a given pg_collation entry.
*
* Returns a palloc'd copy of the string, or NULL if no such constraint.
*
* NOTE: since collation name is not unique, be wary of code that uses this
* for anything except preparing error messages.
*/
char* get_collation_name(Oid colloid)
{
HeapTuple tp;
tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid));
if (HeapTupleIsValid(tp)) {
Form_pg_collation colltup = (Form_pg_collation)GETSTRUCT(tp);
char* result = NULL;
result = pstrdup(NameStr(colltup->collname));
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
/*
* get_constraint_name
* Returns the name of a given pg_constraint entry.
*
* Returns a palloc'd copy of the string, or NULL if no such constraint.
*
* NOTE: since constraint name is not unique, be wary of code that uses this
* for anything except preparing error messages.
*/
char* get_constraint_name(Oid conoid)
{
HeapTuple tp;
tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conoid));
if (HeapTupleIsValid(tp)) {
Form_pg_constraint contup = (Form_pg_constraint)GETSTRUCT(tp);
char* result = NULL;
result = pstrdup(NameStr(contup->conname));
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
/*
* get_opclass_family
*
* Returns the OID of the operator family the opclass belongs to.
*/
Oid get_opclass_family(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
Oid result;
tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for opclass %u", opclass)));
}
cla_tup = (Form_pg_opclass)GETSTRUCT(tp);
result = cla_tup->opcfamily;
ReleaseSysCache(tp);
return result;
}
/*
* get_opclass_input_type
*
* Returns the OID of the datatype the opclass indexes.
*/
Oid get_opclass_input_type(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
Oid result;
tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for opclass %u", opclass)));
}
cla_tup = (Form_pg_opclass)GETSTRUCT(tp);
result = cla_tup->opcintype;
ReleaseSysCache(tp);
return result;
}
/*
* get_opcode
*
* Returns the regproc id of the routine used to implement an
* operator given the operator oid.
*/
RegProcedure get_opcode(Oid opno)
{
HeapTuple tp;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
RegProcedure result;
result = optup->oprcode;
ReleaseSysCache(tp);
return result;
} else {
return (RegProcedure)InvalidOid;
}
}
/*
* get_opname
* returns the name of the operator with the given opno
*
* Note: returns a palloc'd copy of the string, or NULL if no such operator.
*/
char* get_opname(Oid opno)
{
HeapTuple tp;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
char* result = NULL;
result = pstrdup(NameStr(optup->oprname));
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
/*
* op_input_types
*
* Returns the left and right input datatypes for an operator
* (InvalidOid if not relevant).
*/
void op_input_types(Oid opno, Oid* lefttype, Oid* righttype)
{
HeapTuple tp;
Form_pg_operator optup;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (!HeapTupleIsValid(tp)) {
/* shouldn't happen */
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for operator %u", opno)));
}
optup = (Form_pg_operator)GETSTRUCT(tp);
*lefttype = optup->oprleft;
*righttype = optup->oprright;
ReleaseSysCache(tp);
}
/*
* op_mergejoinable
*
* Returns true if the operator is potentially mergejoinable. (The planner
* will fail to find any mergejoin plans unless there are suitable btree
* opfamily entries for this operator and associated sortops. The pg_operator
* flag is just a hint to tell the planner whether to bother looking.)
*
* In some cases (currently only array_eq and record_eq), mergejoinability
* depends on the specific input data type the operator is invoked for, so
* that must be passed as well. We currently assume that only one input's type
* is needed to check this --- by convention, pass the left input's data type.
*/
bool op_mergejoinable(Oid opno, Oid inputtype)
{
bool result = false;
HeapTuple tp;
TypeCacheEntry* typentry = NULL;
/*
* For array_eq or record_eq, we can sort if the element or field types
* are all sortable. We could implement all the checks for that here, but
* the typcache already does that and caches the results too, so let's
* rely on the typcache.
*/
if (opno == ARRAY_EQ_OP) {
typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
if (typentry->cmp_proc == F_BTARRAYCMP) {
result = true;
}
} else if (opno == RECORD_EQ_OP) {
typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
if (typentry->cmp_proc == F_BTRECORDCMP) {
result = true;
}
} else {
/* For all other operators, rely on pg_operator.oprcanmerge */
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
result = optup->oprcanmerge;
ReleaseSysCache(tp);
}
}
return result;
}
/*
* op_hashjoinable
*
* Returns true if the operator is hashjoinable. (There must be a suitable
* hash opfamily entry for this operator if it is so marked.)
*
* In some cases (currently only array_eq), hashjoinability depends on the
* specific input data type the operator is invoked for, so that must be
* passed as well. We currently assume that only one input's type is needed
* to check this --- by convention, pass the left input's data type.
*/
bool op_hashjoinable(Oid opno, Oid inputtype)
{
bool result = false;
HeapTuple tp;
TypeCacheEntry* typentry = NULL;
/* As in op_mergejoinable, let the typcache handle the hard cases */
/* Eventually we'll need a similar case for record_eq ... */
if (opno == ARRAY_EQ_OP) {
typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
if (typentry->hash_proc == F_HASH_ARRAY) {
result = true;
}
} else {
/* For all other operators, rely on pg_operator.oprcanhash */
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
result = optup->oprcanhash;
ReleaseSysCache(tp);
}
}
return result;
}
/*
* op_strict
*
* Get the proisstrict flag for the operator's underlying function.
*/
bool op_strict(Oid opno)
{
RegProcedure funcid = get_opcode(opno);
if (funcid == (RegProcedure)InvalidOid) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("operator %u does not exist", opno)));
}
return func_strict((Oid)funcid);
}
/*
* op_volatile
*
* Get the provolatile flag for the operator's underlying function.
*/
char op_volatile(Oid opno)
{
RegProcedure funcid = get_opcode(opno);
if (funcid == (RegProcedure)InvalidOid) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("operator %u does not exist", opno)));
}
return func_volatile((Oid)funcid);
}
/*
* get_commutator
*
* Returns the corresponding commutator of an operator.
*/
Oid get_commutator(Oid opno)
{
HeapTuple tp;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
Oid result;
result = optup->oprcom;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_negator
*
* Returns the corresponding negator of an operator.
*/
Oid get_negator(Oid opno)
{
HeapTuple tp;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
Oid result;
result = optup->oprnegate;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_equal
*
* Returns the corresponding equal of an operator.
*/
Oid get_equal(Oid opno)
{
if (opno < FirstNormalObjectId) {
HeapTuple tp;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
Oid result, operatorNamespace, leftObjectId, rightObjectId;
bool defined = false;
operatorNamespace = optup->oprnamespace;
leftObjectId = optup->oprleft;
rightObjectId = optup->oprright;
ReleaseSysCache(tp);
result = OperatorGet("=", (Oid)operatorNamespace, (Oid)leftObjectId, (Oid)rightObjectId, &defined);
if (defined) {
return result;
}
}
}
return InvalidOid;
}
/*
* get_oprrest
*
* Returns procedure id for computing selectivity of an operator.
*/
RegProcedure get_oprrest(Oid opno)
{
HeapTuple tp;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
RegProcedure result;
result = optup->oprrest;
ReleaseSysCache(tp);
return result;
} else {
return (RegProcedure)InvalidOid;
}
}
/*
* get_oprjoin
*
* Returns procedure id for computing selectivity of a join.
*/
RegProcedure get_oprjoin(Oid opno)
{
HeapTuple tp;
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp)) {
Form_pg_operator optup = (Form_pg_operator)GETSTRUCT(tp);
RegProcedure result;
result = optup->oprjoin;
ReleaseSysCache(tp);
return result;
} else {
return (RegProcedure)InvalidOid;
}
}
/*
* get_func_name
* returns the name of the function with the given funcid
*
* Note: returns a palloc'd copy of the string, or NULL if no such function.
*/
char* get_func_name(Oid funcid)
{
HeapTuple tp;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (HeapTupleIsValid(tp)) {
Form_pg_proc functup = (Form_pg_proc)GETSTRUCT(tp);
char* result = NULL;
result = pstrdup(NameStr(functup->proname));
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
/*
* get_func_namespace
*
* Returns the pg_namespace OID associated with a given function.
*/
Oid get_func_namespace(Oid funcid)
{
HeapTuple tp;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (HeapTupleIsValid(tp)) {
Form_pg_proc functup = (Form_pg_proc)GETSTRUCT(tp);
Oid result;
result = functup->pronamespace;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_func_rettype
* Given procedure id, return the function's result type.
*/
Oid get_func_rettype(Oid funcid)
{
HeapTuple tp;
Oid result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->prorettype;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_nargs
* Given procedure id, return the number of arguments.
*/
int get_func_nargs(Oid funcid)
{
HeapTuple tp;
int result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->pronargs;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_signature
* Given procedure id, return the function's argument and result types.
* (The return value is the result type.)
*
* The arguments are returned as a palloc'd array.
*/
Oid get_func_signature(Oid funcid, Oid** argtypes, int* nargs)
{
HeapTuple tp;
Form_pg_proc procstruct;
Oid result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
procstruct = (Form_pg_proc)GETSTRUCT(tp);
result = procstruct->prorettype;
*nargs = (int)procstruct->pronargs;
Assert(*nargs == procstruct->proargtypes.dim1);
*argtypes = (Oid*)palloc(*nargs * sizeof(Oid));
if (*nargs > 0) {
int rc = memcpy_s(*argtypes, *nargs * sizeof(Oid), procstruct->proargtypes.values, *nargs * sizeof(Oid));
securec_check(rc, "\0", "\0");
}
ReleaseSysCache(tp);
return result;
}
/*
* get_func_variadictype
* Given procedure id, return the function's provariadic field.
*/
Oid get_func_variadictype(Oid funcid)
{
HeapTuple tp;
Oid result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->provariadic;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_retset
* Given procedure id, return the function's proretset flag.
*/
bool get_func_retset(Oid funcid)
{
HeapTuple tp;
bool result = false;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->proretset;
ReleaseSysCache(tp);
return result;
}
/*
* func_strict
* Given procedure id, return the function's proisstrict flag.
*/
bool func_strict(Oid funcid)
{
HeapTuple tp;
bool result = false;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->proisstrict;
ReleaseSysCache(tp);
return result;
}
/*
* func_volatile
* Given procedure id, return the function's provolatile flag.
*/
char func_volatile(Oid funcid)
{
HeapTuple tp;
char result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->provolatile;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_proshippable
* Given procedure id, return the function's proshippable flag.
*/
bool get_func_proshippable(Oid funcid)
{
HeapTuple tp;
bool result = false;
bool isNull = true;
Relation relation = heap_open(ProcedureRelationId, AccessShareLock);
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
Datum datum = heap_getattr(tp, Anum_pg_proc_shippable, RelationGetDescr(relation), &isNull);
result = DatumGetBool(datum);
heap_close(relation, AccessShareLock);
ReleaseSysCache(tp);
return result;
}
/*
* get_func_leakproof
* Given procedure id, return the function's leakproof field.
*/
bool get_func_leakproof(Oid funcid)
{
HeapTuple tp;
bool result = false;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->proleakproof;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_cost
* Given procedure id, return the function's procost field.
*/
float4 get_func_cost(Oid funcid)
{
HeapTuple tp;
float4 result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->procost;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_rows
* Given procedure id, return the function's prorows field.
*/
float4 get_func_rows(Oid funcid)
{
HeapTuple tp;
float4 result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->prorows;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_lang
* Given procedure id, return the function's prolang field.
*/
Oid get_func_lang(Oid funcid)
{
HeapTuple tp = NULL;
Oid result;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = ((Form_pg_proc)GETSTRUCT(tp))->prolang;
ReleaseSysCache(tp);
return result;
}
/*
* get_func_iswindow
* Given procedure id, return the function is window or not.
*/
bool get_func_iswindow(Oid funcid)
{
HeapTuple tp = NULL;
bool result = false;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
result = PROC_IS_WIN(((Form_pg_proc)GETSTRUCT(tp))->prokind);
ReleaseSysCache(tp);
return result;
}
/*
* get_relname_relid
* Given name and namespace of a relation, look up the OID.
*
* Returns InvalidOid if there is no such relation.
*/
Oid get_relname_relid(const char* relname, Oid relnamespace)
{
return GetSysCacheOid2(RELNAMENSP, PointerGetDatum(relname), ObjectIdGetDatum(relnamespace));
}
/*
* get_relname_relid_extend
* Given name and namespace of a relation, look up the OID and record the details info.
*
* Store OID if relation found in search path, else InvalidOid.
* Returns the details info of supported cases checking.
*/
char* get_relname_relid_extend(
const char* relname, Oid relnamespace, Oid* relOid, bool isSupportSynonym, Oid* refSynOid)
{
Oid relid = InvalidOid;
RangeVar* objVar = NULL;
Oid objNamespaceOid = InvalidOid;
StringInfoData detail;
if (relOid != NULL) {
*relOid = InvalidOid;
}
initStringInfo(&detail);
relid = get_relname_relid(relname, relnamespace);
/* additionally, take it as a synonym and search for its referenced object name. */
if (isSupportSynonym && !OidIsValid(relid)) {
objVar = SearchReferencedObject(relname, relnamespace);
/* if found, then fetch the relid of the objVar and check whether or not we has supported already. */
if (objVar != NULL) {
objNamespaceOid = get_namespace_oid(objVar->schemaname, true);
relid = get_relname_relid(objVar->relname, objNamespaceOid);
appendStringInfo(&detail, _("%s"), CheckReferencedObject(relid, objVar, relname));
/* store the referenced synonym oid when mapping successfully. */
if (refSynOid != NULL && OidIsValid(relid)) {
*refSynOid = GetSynonymOid(relname, relnamespace, true);
}
}
}
if (relOid != NULL) {
*relOid = relid;
}
/* return checking details. */
return detail.data;
}
extern bool StreamTopConsumerAmI();
/* same as get_relname_relid except we check for cache invalidation here */
Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
{
Oid nspid = InvalidOid;
Oid oldnspid = InvalidOid;
Oid relid = InvalidOid;
Oid oldrelid = InvalidOid;
uint64 inval_count = 0;
bool retry = false;
Assert(relnamespace != NULL);
Assert(relname != NULL);
/*
* DDL operations can change the results of a name lookup. Since all such
* operations will generate invalidation messages, we keep track of
* whether any such messages show up while we're performing the operation,
* and retry until either (1) no more invalidation messages show up or (2)
* the answer doesn't change.
*
*/
for (;;) {
inval_count = u_sess->inval_cxt.SharedInvalidMessageCounter;
nspid = get_namespace_oid(relnamespace, false);
relid = get_relname_relid(relname, nspid);
/*
* In bootstrap processing mode, we don't bother with locking
*/
if (IsBootstrapProcessingMode()) {
break;
}
/*
* If, upon retry, we get back the same OID we did last time, then the
* invalidation messages we processed did not change the final answer.
* So we're done.
*
* If we got a different OID, we've locked the relation that used to
* have this name rather than the one that does now. So release the
* lock.
*/
if (retry) {
/* we get the same relid, nothing has changed, so return it */
if (relid == oldrelid && nspid == oldnspid) {
break;
}
if (!OidIsValid(nspid)) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("namespace %s was invalid after retry", relnamespace)));
}
if (!OidIsValid(relid)) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("relation %s was invalid after retry", relname)));
}
/* If creation namespace has changed, give up old lock. */
if (nspid != oldnspid && OidIsValid(oldnspid)) {
UnlockDatabaseObject(NamespaceRelationId, oldnspid, 0, AccessShareLock);
}
if (relid != oldrelid && OidIsValid(oldrelid)) {
UnlockRelationOid(oldrelid, AccessShareLock);
}
}
/* Lock relation */
if (OidIsValid(relid) && StreamTopConsumerAmI()) {
/*
* Keep the oid fetching in plan-serialize stage blocked
* until the former blocking transaction end, we have to
* do so, since that it could be a vacuum-full operation
* where the relid gets changed, thus the de-serializing
* may run into none-matching relid exception.
*/
LockRelationOid(relid, AccessShareLock);
}
/* Lock namespace */
if (OidIsValid(nspid) && StreamTopConsumerAmI()) {
LockDatabaseObject(NamespaceRelationId, nspid, 0, AccessShareLock);
}
/* If no invalidation message were processed, we're done! */
if (inval_count == u_sess->inval_cxt.SharedInvalidMessageCounter) {
break;
}
/*
* Let's repeat the name lookup, to make
* sure this name still references the same relation it did
* previously.
*/
retry = true;
oldrelid = relid;
oldnspid = nspid;
}
return relid;
}
#ifdef PGXC
/*
* get_relnatts
*
* Returns the number of attributes for a given relation.
*/
int get_relnatts(Oid relid)
{
HeapTuple tp;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp)) {
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tp);
int result;
CatalogRelationBuildParam catalogParam = GetCatalogParam(relid);
if (catalogParam.oId != InvalidOid) {
result = (AttrNumber)catalogParam.natts;
} else {
result = reltup->relnatts;
}
ReleaseSysCache(tp);
return result;
} else {
return InvalidAttrNumber;
}
}
#endif
/*
* get_rel_name
* Returns the name of a given relation.
*
* Returns a palloc'd copy of the string, or NULL if no such relation.
*
* NOTE: since relation name is not unique, be wary of code that uses this
* for anything except preparing error messages.
*/
char* get_rel_name(Oid relid)
{
HeapTuple tp;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp)) {
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tp);
char* result = NULL;
result = pstrdup(NameStr(reltup->relname));
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
/*
* @@GaussDB@@
* Target : data partition
* Brief : Returns the name of a given partition
* Description : the partition is eihher a table partiton or an index partition
* Notes : Returns a palloc'd copy of the string, or NULL if no such relation.
*/
char* getPartitionName(Oid partid, bool missing_ok)
{
HeapTuple tuple = NULL;
char* pname = NULL;
bool found = false;
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid));
if (HeapTupleIsValid(tuple)) {
Form_pg_partition partition = (Form_pg_partition)GETSTRUCT(tuple);
/* make sure it is a partition */
Assert((PART_OBJ_TYPE_TABLE_PARTITION == partition->parttype) ||
(PART_OBJ_TYPE_INDEX_PARTITION == partition->parttype));
/* get partition name */
pname = pstrdup(NameStr(partition->relname));
found = true;
ReleaseSysCache(tuple);
}
if (!found && !missing_ok) {
ereport(
ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("object with oid %u is not a partition object", partid)));
}
return pname;
}
/*
* check_rel_is_partitioned
*
* check whether a given relation is partitioned.
*/
bool check_rel_is_partitioned(Oid relid)
{
HeapTuple tp = SearchSysCache1WithLogLevel(RELOID, ObjectIdGetDatum(relid), LOG);
if (HeapTupleIsValid(tp)) {
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tp);
bool result = (reltup->parttype == PARTTYPE_PARTITIONED_RELATION);
ReleaseSysCache(tp);
return result;
} else {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", relid)));
return false; // to ease compiler complain
}
}
/*
* get_rel_namespace
*
* Returns the pg_namespace OID associated with a given relation.
*/
Oid get_rel_namespace(Oid relid)
{
HeapTuple tp;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp)) {
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tp);
Oid result;
result = reltup->relnamespace;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* @Description: check the table's namespace is system catalog or table is not a system table.
* @in - relid: table oid
* @out - bool: return true if it satisfies check condition.
*/
bool is_sys_table(Oid relid)
{
HeapTuple tuple;
Oid namespaceoid;
char* schemaname = NULL;
namespaceoid = get_rel_namespace(relid);
schemaname = get_namespace_name(namespaceoid);
/*
* NodeGroup support
*
* In multi-nodegroup environment, we need fetch table's exec_nodes before apply lock,
* so when concurrent with DROP operation we may get relid but the table has been dropped
* in another transaction, so we need check if it is not exist here.
*/
if (schemaname == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("Table object with oid %u does not exists (has been dropped)", relid)));
}
if (namespaceoid == PG_CATALOG_NAMESPACE) {
pfree_ext(schemaname);
return true;
}
if (strncmp(schemaname, "information_schema", sizeof("information_schema")) == 0) {
pfree_ext(schemaname);
tuple = SearchSysCache1(PGXCCLASSRELID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tuple)) {
ReleaseSysCache(tuple);
return false;
} else {
return true;
}
} else if (IsToastNamespace(namespaceoid) && relid < FirstNormalObjectId) {
pfree_ext(schemaname);
/* Bootstrap toast tables are also considered as system tables. */
return true;
} else {
pfree_ext(schemaname);
return false;
}
}
/*
* get_rel_type_id
*
* Returns the pg_type OID associated with a given relation.
*
* Note: not all pg_class entries have associated pg_type OIDs; so be
* careful to check for InvalidOid result.
*/
Oid get_rel_type_id(Oid relid)
{
HeapTuple tp;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp)) {
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tp);
Oid result;
result = reltup->reltype;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_rel_relkind
*
* Returns the relkind associated with a given relation.
*/
char get_rel_relkind(Oid relid)
{
HeapTuple tp;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp)) {
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tp);
char result;
result = reltup->relkind;
ReleaseSysCache(tp);
return result;
} else {
return '\0';
}
}
/*
* get_rel_tablespace
*
* Returns the pg_tablespace OID associated with a given relation.
*
* Note: InvalidOid might mean either that we couldn't find the relation,
* or that it is in the database's default tablespace.
*/
Oid get_rel_tablespace(Oid relid)
{
HeapTuple tp;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp)) {
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tp);
Oid result;
result = reltup->reltablespace;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_typisdefined
*
* Given the type OID, determine whether the type is defined
* (if not, it's only a shell).
*/
bool get_typisdefined(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
bool result = false;
result = typtup->typisdefined;
ReleaseSysCache(tp);
return result;
} else {
return false;
}
}
/*
* get_typlen
*
* Given the type OID, return the length of the type.
*/
int16 get_typlen(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
int16 result;
result = typtup->typlen;
ReleaseSysCache(tp);
return result;
} else {
return 0;
}
}
/*
* get_typbyval
*
* Given the type OID, determine whether the type is returned by value or
* not. Returns true if by value, false if by reference.
*/
bool get_typbyval(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
bool result = false;
result = typtup->typbyval;
ReleaseSysCache(tp);
return result;
} else {
return false;
}
}
/*
* get_typlenbyval
*
* A two-fer: given the type OID, return both typlen and typbyval.
*
* Since both pieces of info are needed to know how to copy a Datum,
* many places need both. Might as well get them with one cache lookup
* instead of two. Also, this routine raises an error instead of
* returning a bogus value when given a bad type OID.
*/
void get_typlenbyval(Oid typid, int16* typlen, bool* typbyval)
{
HeapTuple tp;
Form_pg_type typtup;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typtup = (Form_pg_type)GETSTRUCT(tp);
*typlen = typtup->typlen;
*typbyval = typtup->typbyval;
ReleaseSysCache(tp);
}
/*
* get_typlenbyvalalign
*
* A three-fer: given the type OID, return typlen, typbyval, typalign.
*/
void get_typlenbyvalalign(Oid typid, int16* typlen, bool* typbyval, char* typalign)
{
HeapTuple tp;
Form_pg_type typtup;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typtup = (Form_pg_type)GETSTRUCT(tp);
*typlen = typtup->typlen;
*typbyval = typtup->typbyval;
*typalign = typtup->typalign;
ReleaseSysCache(tp);
}
/*
* getTypeIOParam
* Given a pg_type row, select the type OID to pass to I/O functions
*
* Formerly, all I/O functions were passed pg_type.typelem as their second
* parameter, but we now have a more complex rule about what to pass.
* This knowledge is intended to be centralized here --- direct references
* to typelem elsewhere in the code are wrong, if they are associated with
* I/O calls and not with actual subscripting operations! (But see
* bootstrap.c's boot_get_type_io_data() if you need to change this.)
*
* As of PostgreSQL 8.1, output functions receive only the value itself
* and not any auxiliary parameters, so the name of this routine is now
* a bit of a misnomer ... it should be getTypeInputParam.
*/
Oid getTypeIOParam(HeapTuple typeTuple)
{
Form_pg_type typeStruct = (Form_pg_type)GETSTRUCT(typeTuple);
/*
* Array types get their typelem as parameter; everybody else gets their
* own type OID as parameter.
*/
if (OidIsValid(typeStruct->typelem)) {
return typeStruct->typelem;
} else {
return HeapTupleGetOid(typeTuple);
}
}
/*
* get_type_io_data
*
* A six-fer: given the type OID, return typlen, typbyval, typalign,
* typdelim, typioparam, and IO function OID. The IO function
* returned is controlled by IOFuncSelector
*/
void get_type_io_data(Oid typid, IOFuncSelector which_func, int16* typlen, bool* typbyval, char* typalign,
char* typdelim, Oid* typioparam, Oid* func)
{
HeapTuple typeTuple;
Form_pg_type typeStruct;
/*
* In bootstrap mode, pass it off to bootstrap.c. This hack allows us to
* use array_in and array_out during bootstrap.
*/
if (IsBootstrapProcessingMode()) {
Oid typinput;
Oid typoutput;
boot_get_type_io_data(typid, typlen, typbyval, typalign, typdelim, typioparam, &typinput, &typoutput);
switch (which_func) {
case IOFunc_input:
*func = typinput;
break;
case IOFunc_output:
*func = typoutput;
break;
default:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("binary I/O not supported during bootstrap")));
break;
}
return;
}
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typeStruct = (Form_pg_type)GETSTRUCT(typeTuple);
*typlen = typeStruct->typlen;
*typbyval = typeStruct->typbyval;
*typalign = typeStruct->typalign;
*typdelim = typeStruct->typdelim;
*typioparam = getTypeIOParam(typeTuple);
switch (which_func) {
case IOFunc_input:
*func = typeStruct->typinput;
break;
case IOFunc_output:
*func = typeStruct->typoutput;
break;
case IOFunc_receive:
*func = typeStruct->typreceive;
break;
case IOFunc_send:
*func = typeStruct->typsend;
break;
default:
break;
}
ReleaseSysCache(typeTuple);
}
#ifdef NOT_USED
char get_typalign(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
char result;
result = typtup->typalign;
ReleaseSysCache(tp);
return result;
} else {
return 'i';
}
}
#endif
char get_typstorage(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
char result;
result = typtup->typstorage;
ReleaseSysCache(tp);
return result;
} else {
return 'p';
}
}
/*
* get_typdefault
* Given a type OID, return the type's default value, if any.
*
* The result is a palloc'd expression node tree, or NULL if there
* is no defined default for the datatype.
*
* NB: caller should be prepared to coerce result to correct datatype;
* the returned expression tree might produce something of the wrong type.
*/
Node* get_typdefault(Oid typid)
{
HeapTuple typeTuple;
Form_pg_type type;
Datum datum;
bool isNull = false;
Node* expr = NULL;
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
type = (Form_pg_type)GETSTRUCT(typeTuple);
/*
* typdefault and typdefaultbin are potentially null, so don't try to
* access 'em as struct fields. Must do it the hard way with
* SysCacheGetAttr.
*/
datum = SysCacheGetAttr(TYPEOID, typeTuple, Anum_pg_type_typdefaultbin, &isNull);
if (!isNull) {
/* We have an expression default */
expr = (Node*)stringToNode(TextDatumGetCString(datum));
} else {
/* Perhaps we have a plain literal default */
datum = SysCacheGetAttr(TYPEOID, typeTuple, Anum_pg_type_typdefault, &isNull);
if (!isNull) {
char* strDefaultVal = NULL;
/* Convert text datum to C string */
strDefaultVal = TextDatumGetCString(datum);
/* Convert C string to a value of the given type */
datum = OidInputFunctionCall(type->typinput, strDefaultVal, getTypeIOParam(typeTuple), -1);
/* Build a Const node containing the value */
expr = (Node*)makeConst(typid, -1, type->typcollation, type->typlen, datum, false, type->typbyval);
pfree_ext(strDefaultVal);
} else {
/* No default */
expr = NULL;
}
}
ReleaseSysCache(typeTuple);
return expr;
}
/*
* getBaseType
* If the given type is a domain, return its base type;
* otherwise return the type's own OID.
*/
Oid getBaseType(Oid typid)
{
int32 typmod = -1;
return getBaseTypeAndTypmod(typid, &typmod);
}
/*
* getBaseTypeAndTypmod
* If the given type is a domain, return its base type and typmod;
* otherwise return the type's own OID, and leave *typmod unchanged.
*
* Note that the "applied typmod" should be -1 for every domain level
* above the bottommost; therefore, if the passed-in typid is indeed
* a domain, *typmod should be -1.
*/
Oid getBaseTypeAndTypmod(Oid typid, int32* typmod)
{
/*
* We loop to find the bottom base type in a stack of domains.
*/
for (;;) {
HeapTuple tup;
Form_pg_type typTup;
tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tup)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typTup = (Form_pg_type)GETSTRUCT(tup);
if (typTup->typtype != TYPTYPE_DOMAIN) {
/* Not a domain, so done */
ReleaseSysCache(tup);
break;
}
Assert(*typmod == -1);
typid = typTup->typbasetype;
*typmod = typTup->typtypmod;
ReleaseSysCache(tup);
}
return typid;
}
#ifdef PGXC
/*
* get_cfgname
* Get cfg name for given cfg ID
*/
char* get_cfgname(Oid cfgid)
{
HeapTuple tuple;
Form_pg_ts_config cfgform;
char* result = NULL;
tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for cfg %u", cfgid)));
}
cfgform = (Form_pg_ts_config)GETSTRUCT(tuple);
result = NameStr(cfgform->cfgname);
ReleaseSysCache(tuple);
return result;
}
/*
* get_typename
* Get type name for given type ID
*/
char* get_typename(Oid typid)
{
HeapTuple tuple;
Form_pg_type typeForm;
char* result = NULL;
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typeForm = (Form_pg_type)GETSTRUCT(tuple);
result = pstrdup(NameStr(typeForm->typname));
ReleaseSysCache(tuple);
return result;
}
/*
* get_typename
* Get enumtype label name for given enumtype labeliD
*/
char* get_enumlabelname(Oid enumlabelid)
{
HeapTuple tuple;
Form_pg_enum en;
char* result = NULL;
tuple = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumlabelid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for enumlabelid %u", enumlabelid)));
}
en = (Form_pg_enum)GETSTRUCT(tuple);
result = pstrdup(NameStr(en->enumlabel));
ReleaseSysCache(tuple);
return result;
}
/*
* get_typename
* Get enumtype name for given enumtype labeliD
*/
char* get_exprtypename(Oid enumlabelid)
{
HeapTuple tuple;
Form_pg_enum en;
Form_pg_type typeForm;
Oid enumtypid;
char* result = NULL;
tuple = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumlabelid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for enumlabelid %u", enumlabelid)));
}
en = (Form_pg_enum)GETSTRUCT(tuple);
enumtypid = en->enumtypid;
ReleaseSysCache(tuple);
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enumtypid));
if (!HeapTupleIsValid(tuple)) {
ereport(
ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for enumtype %u", enumtypid)));
}
typeForm = (Form_pg_type)GETSTRUCT(tuple);
result = pstrdup(NameStr(typeForm->typname));
ReleaseSysCache(tuple);
return result;
}
/*
* get_typename
* Get type name with namespace for given type ID
*/
char* get_typename_with_namespace(Oid typid)
{
HeapTuple tuple;
Form_pg_type typeForm;
char* result = NULL;
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typeForm = (Form_pg_type)GETSTRUCT(tuple);
// Produce a string representing the name of a TypeName. It is always with
// format namespace.typename, except for "pg_catalog" namespace. This is
// because most common data types are from it, so we optimize it to avoid
// extra overhead to lookup for it on both sides.
//
// Temp namespace needs special handling here, we can't omit its namespace
// as it may contains a literally same typname as pg_catalog. Send the name
// directly won't work as its name can vary on different nodes. We send a
// special flag instead.
//
if (typeForm->typnamespace == PG_CATALOG_NAMESPACE) {
result = pstrdup(NameStr(typeForm->typname));
} else {
StringInfoData typeName;
char* typeNamespace = "%";
if (!isTempNamespace(typeForm->typnamespace)) {
typeNamespace = get_namespace_name(typeForm->typnamespace, true);
Assert(typeNamespace[0] != '%');
}
initStringInfo(&typeName);
appendStringInfoString(&typeName, typeNamespace);
appendStringInfoString(&typeName, ".");
appendStringInfoString(&typeName, NameStr(typeForm->typname));
result = pstrdup(typeName.data);
if (typeNamespace[0] != '%') {
pfree_ext(typeNamespace);
}
pfree_ext(typeName.data);
}
ReleaseSysCache(tuple);
return result;
}
/*
* get_typeoid_with_namespace
* Get type oid with namespace for given type name. This actually works as
* a "reversal" version of get_typename_with_namespace().
*/
Oid get_typeoid_with_namespace(const char* typname)
{
Oid typid;
Oid namespaceId = InvalidOid;
int elen;
List* elemlist = NULL;
char* nspname = NULL;
char* atpname = NULL;
// Separate namespace and typename from given string
//
char* namestr = pstrdup(typname);
if (!SplitIdentifierString(namestr, '.', &elemlist, false)) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid syntax for type: \"%s\"", namestr)));
}
elen = list_length(elemlist);
if (elen == 1) {
atpname = (char*)linitial(elemlist);
namespaceId = PG_CATALOG_NAMESPACE;
} else if (elen == 2) {
nspname = (char*)linitial(elemlist);
atpname = (char*)lsecond(elemlist);
// Take care of temp namespace specially
//
if (nspname[0] == '%') {
namespaceId = get_my_temp_schema();
} else {
namespaceId = get_namespace_oid(nspname, false);
}
} else {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid syntax for type: \"%s\"", namestr)));
}
// Search the unique type oid by namespace and typename.
//
Assert(OidIsValid(namespaceId));
typid = GetSysCacheOid2(TYPENAMENSP, PointerGetDatum(atpname), ObjectIdGetDatum(namespaceId));
pfree_ext(namestr);
return typid;
}
/*
* get_pgxc_nodeoid
* Obtain PGXC Node Oid for given node name
* Return Invalid Oid if object does not exist
*/
Oid get_pgxc_nodeoid(const char* nodename)
{
Oid node_oid = InvalidOid;
CatCList* memlist = NULL;
int i;
memlist = SearchSysCacheList1(PGXCNODENAME, PointerGetDatum(nodename));
for (i = 0; i < memlist->n_members; i++) {
HeapTuple tup = &memlist->members[i]->tuple;
char node_type = ((Form_pgxc_node)GETSTRUCT(tup))->node_type;
if (node_type == PGXC_NODE_COORDINATOR || node_type == PGXC_NODE_DATANODE) {
node_oid = HeapTupleGetOid(tup);
break;
}
}
ReleaseSysCacheList(memlist);
return node_oid;
}
/*
* Check the datanode is exist.
* this case only make sense under multi-standbys mode.
*/
bool check_pgxc_node_name_is_exist(
const char* nodename, const char* host, int port, int comm_sctp_port, int comm_control_port)
{
CatCList* memlist = NULL;
bool node_has_been_def = false;
int i;
Assert(host != NULL);
memlist = SearchSysCacheList1(PGXCNODENAME, PointerGetDatum(nodename));
for (i = 0; i < memlist->n_members; i++) {
HeapTuple tup = &memlist->members[i]->tuple;
const char* host_exist = NameStr(((Form_pgxc_node)GETSTRUCT(tup))->node_host);
int port_exist = ((Form_pgxc_node)GETSTRUCT(tup))->node_port;
int comm_sctp_port_exist = ((Form_pgxc_node)GETSTRUCT(tup))->sctp_port;
int comm_control_port_exist = ((Form_pgxc_node)GETSTRUCT(tup))->control_port;
/* check host */
if ((strncmp(host_exist, "localhost", strlen("localhost")) == 0 &&
strncmp(host, "127.0.0.1", strlen("127.0.0.1")) == 0) ||
(strncmp(host_exist, host, strlen(host)) == 0)) {
/* check port */
if (port_exist == port) {
node_has_been_def = true;
break;
}
/* check comm ports */
if (comm_sctp_port_exist == comm_sctp_port || comm_control_port_exist == comm_control_port) {
node_has_been_def = true;
break;
}
}
}
ReleaseSysCacheList(memlist);
return node_has_been_def;
}
Datum node_oid_name(PG_FUNCTION_ARGS)
{
Oid nodeoid = PG_GETARG_OID(0);
PG_RETURN_NAME(get_pgxc_nodename(nodeoid));
}
/*
* get_pgxc_groupname
* Get nodegroup name by given nodeoid, return NULL if nodegroup not found or
* given an invalid oid.
*/
char* get_pgxc_groupname(Oid groupoid, char* groupname)
{
HeapTuple tuple;
Form_pgxc_group groupForm;
if (!OidIsValid(groupoid)) {
return NULL;
}
tuple = SearchSysCache1(PGXCGROUPOID, ObjectIdGetDatum(groupoid));
if (!HeapTupleIsValid(tuple)) {
return NULL;
}
groupForm = (Form_pgxc_group)GETSTRUCT(tuple);
if (groupname == NULL) {
groupname = pstrdup(NameStr(groupForm->group_name));
} else {
errno_t errval = strncpy_s(groupname, NAMEDATALEN, NameStr(groupForm->group_name), NAMEDATALEN - 1);
securec_check_errval(errval,, LOG);
}
ReleaseSysCache(tuple);
return groupname;
}
void get_node_info(const char* nodename, bool* node_is_ccn, ItemPointer tuple_pos)
{
HeapTuple tuple;
Relation rel;
bool is_null = false;
CatCList* memlist = NULL;
Assert(nodename);
Assert(node_is_ccn);
Assert(tuple_pos);
if (!StringIsValid(nodename)) {
return;
}
if (node_is_ccn == NULL || tuple_pos == NULL) {
return;
}
if (t_thrd.proc == NULL) {
*node_is_ccn = false;
return;
}
memlist = NULL;
rel = heap_open(PgxcNodeRelationId, AccessShareLock);
PG_TRY();
{
memlist = SearchSysCacheList1(PGXCNODENAME, PointerGetDatum(nodename));
if (memlist->n_members != 1) {
*node_is_ccn = false;
ReleaseSysCacheList(memlist);
heap_close(rel, AccessShareLock);
return;
}
tuple = &memlist->members[0]->tuple;
/* get nodeis_central for the node */
Datum centralDatum = SysCacheGetAttr(PGXCNODEOID, tuple, Anum_pgxc_node_is_central, &is_null);
if (!is_null) {
*node_is_ccn = DatumGetBool(centralDatum);
ItemPointerCopy(&tuple->t_self, tuple_pos);
} else {
*node_is_ccn = false;
}
ReleaseSysCacheList(memlist);
heap_close(rel, AccessShareLock);
}
PG_CATCH();
{
if (memlist != NULL) {
ReleaseSysCacheList(memlist);
}
heap_close(rel, AccessShareLock);
PG_RE_THROW();
}
PG_END_TRY();
return;
}
/*
* get_pgxc_nodename
* Get node name for given Oid
*/
char* get_pgxc_nodename(Oid nodeid, NameData* namedata)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
errno_t rc;
char* nodename = NULL;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
if (namedata != NULL) {
nodename = namedata->data;
rc = strncpy_s(nodename, NAMEDATALEN, NameStr(nodeForm->node_name), NAMEDATALEN - 1);
securec_check(rc, "\0", "\0");
} else {
nodename = pstrdup(NameStr(nodeForm->node_name));
}
ReleaseSysCache(tuple);
return nodename;
}
/*
* get_pgxc_nodename_noexcept
* Get node name for given Oid, for print log.
*/
char* get_pgxc_nodename_noexcept(Oid nodeid, char* nodename)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
errno_t rc;
if (t_thrd.utils_cxt.CurrentResourceOwner == NULL || nodename == NULL) {
return NULL;
}
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
return NULL;
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
rc = strncpy_s(nodename, NAMEDATALEN, NameStr(nodeForm->node_name), NAMEDATALEN - 1);
securec_check(rc, "\0", "\0");
ReleaseSysCache(tuple);
return nodename;
}
bool is_pgxc_central_nodeid(Oid nodeid)
{
if (t_thrd.proc == NULL) {
return false;
}
HeapTuple tuple;
bool is_central = false;
bool is_null = false;
if (!OidIsValid(nodeid)) {
return false;
}
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
return false;
}
/* get nodeis_central for the node */
Datum centralDatum = SysCacheGetAttr(PGXCNODEOID, tuple, Anum_pgxc_node_is_central, &is_null);
if (!is_null) {
is_central = DatumGetBool(centralDatum);
}
ReleaseSysCache(tuple);
return is_central;
}
bool is_pgxc_central_nodename(const char* nodename)
{
bool is_central = false;
Datum central_datum;
CatCList* memlist = NULL;
HeapTuple tup;
memlist = SearchSysCacheList1(PGXCNODENAME, PointerGetDatum(nodename));
if (memlist->n_members != 1) {
/*
* If we get more then one node named by 'nodename', we meet datanode under
* repliation_type == 1. we just return false;
* If we find nothing, return false too.
*/
is_central = false;
goto result;
}
tup = &memlist->members[0]->tuple;
central_datum = ((Form_pgxc_node)GETSTRUCT(tup))->nodeis_central;
is_central = DatumGetBool(central_datum);
result:
ReleaseSysCacheList(memlist);
return is_central;
}
char* get_pgxc_node_formdata(const char* nodename)
{
if (!StringIsValid(nodename)) {
return NULL;
}
return get_pgxc_nodehost(get_pgxc_nodeoid(nodename));
}
/*
* Description: Get node name for a given Oid.
*
* Parameters:
* @in node_id: Node Oid.
* @in handle_error: need report error when tuple is invalid(default: true)
* Returns: char*
*/
char* get_pgxc_node_name_by_node_id(int4 node_id, bool handle_error)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
char* result = NULL;
tuple = SearchSysCache1(PGXCNODEIDENTIFIER, Int32GetDatum(node_id));
if (!HeapTupleIsValid(tuple)) {
if (handle_error) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", node_id)));
} else {
return NULL;
}
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = pstrdup(NameStr(nodeForm->node_name));
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_node_id
* Get node identifier for a given Oid
*/
uint32 get_pgxc_node_id(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
uint32 result;
if (nodeid == InvalidOid) {
return 0;
}
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->node_id;
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodetype
* Get node type for given Oid
*/
char get_pgxc_nodetype(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
char result;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->node_type;
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodetype
* Get node type for given Oid
*/
char get_pgxc_nodetype_refresh_cache(Oid nodeid)
{
AcceptInvalidationMessages();
return get_pgxc_nodetype(nodeid);
}
/*
* get_pgxc_nodeport
* Get node port for given Oid
*/
int get_pgxc_nodeport(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
int result;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->node_port;
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodehost
* Get node host for given Oid
*/
char* get_pgxc_nodehost(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
char* result = NULL;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = pstrdup(NameStr(nodeForm->node_host));
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodeport1
* Get node port for given Oid
*/
int get_pgxc_nodeport1(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
int result;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->node_port1;
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodehost
* Get node host for given Oid
*/
char* get_pgxc_nodehost1(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
char* result = NULL;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = pstrdup(NameStr(nodeForm->node_host1));
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodesctpport
* Get node sctp port for given Oid
*/
int get_pgxc_nodesctpport(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
int result;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->sctp_port;
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodesctpport
* Get node strmctl port for given Oid
*/
int get_pgxc_nodestrmctlport(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
int result;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->control_port;
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodesctpport
* Get node sctp port for given Oid
*/
int get_pgxc_nodesctpport1(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
int result;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->sctp_port1;
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_nodesctpport
* Get node strmctl port for given Oid
*/
int get_pgxc_nodestrmctlport1(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
int result;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->control_port1;
ReleaseSysCache(tuple);
return result;
}
/*
* is_pgxc_nodepreferred
* Determine if node is a preferred one
*/
bool is_pgxc_nodepreferred(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
bool result = false;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->nodeis_preferred;
ReleaseSysCache(tuple);
return result;
}
/*
* is_pgxc_hostprimary
* Determine if node host is a primary ip
*/
bool is_pgxc_hostprimary(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
bool result = false;
LockRelationOid(PgxcNodeRelationId, AccessShareLock);
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
UnlockRelationOid(PgxcNodeRelationId, AccessShareLock);
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->hostis_primary;
ReleaseSysCache(tuple);
UnlockRelationOid(PgxcNodeRelationId, AccessShareLock);
return result;
}
/*
* is_pgxc_nodeprimary
* Determine if node is a primary one
*/
bool is_pgxc_nodeprimary(Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
bool result = false;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
result = nodeForm->nodeis_primary;
ReleaseSysCache(tuple);
return result;
}
bool is_pgxc_nodeactive(Oid nodeid)
{
HeapTuple tuple;
bool isactive = false;
bool isNull = false;
if (!OidIsValid(nodeid)) {
return false;
}
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
Datum activeDatum = SysCacheGetAttr(PGXCNODEOID, tuple, Anum_pgxc_node_is_active, &isNull);
if (!isNull) {
isactive = DatumGetBool(activeDatum);
}
ReleaseSysCache(tuple);
return isactive;
}
/*
* @Description: check host is match with node's Primary Host
* @in host: host
* @in nodeid: nodeid
* @return: true if match, false if not
*
* Notes: suppose primary and standby deployed on different machine,
* so, we just check host, no need to check port.
*/
bool node_check_host(const char* host, Oid nodeid)
{
HeapTuple tuple;
Form_pgxc_node nodeForm;
bool result = true;
char node_type = PGXC_NODE_COORDINATOR;
bool is_primary_dn = false;
tuple = SearchSysCache1(PGXCNODEOID, ObjectIdGetDatum(nodeid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for node %u", nodeid)));
}
nodeForm = (Form_pgxc_node)GETSTRUCT(tuple);
node_type = nodeForm->node_type;
if (node_type == PGXC_NODE_COORDINATOR) {
is_primary_dn = true;
} else {
if (IS_DN_DUMMY_STANDYS_MODE()) {
is_primary_dn = nodeForm->hostis_primary;
} else {
/* multi standby */
is_primary_dn = IS_PRIMARY_DATA_NODE(node_type);
}
}
if (is_primary_dn) {
/* host is primary, check node_host is match with input */
if (strncmp(NameStr(nodeForm->node_host), host, strlen(host)) != 0) {
result = false;
}
} else {
/* standby mode */
if (IS_DN_DUMMY_STANDYS_MODE()) {
if (strncmp(NameStr(nodeForm->node_host1), host, strlen(host)) != 0) {
result = false;
}
} else {
/* multi standby */
Oid primary_oid = PgxcNodeGetPrimaryDNFromMatric(nodeid);
char* primary_host = get_pgxc_nodehost(primary_oid);
if (primary_host != NULL) {
if (strncmp(primary_host, host, strlen(host)) != 0) {
result = false;
}
pfree_ext(primary_host);
primary_host = NULL;
}
}
}
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_groupoid
* Obtain PGXC Group Oid for given group name
* Return Invalid Oid if group does not exist
*/
Oid get_pgxc_groupoid(const char* groupname, bool missing_ok)
{
#ifdef ENABLE_MULTIPLE_NODES
uint64 version = GetPGXCGlobalVersion();
Oid groupoid = GetCachedGrpidByName(groupname, version);
if (groupoid != (Oid)-1 && groupoid != 0)
return groupoid;
#else
Oid groupoid = InvalidOid;
#endif
if (!ng_is_valid_group_name(groupname)) {
return InvalidOid;
}
#ifdef ENABLE_MULTIPLE_NODES
version = GetPGXCGlobalVersion();
#endif
groupoid = GetSysCacheOid1(PGXCGROUPNAME, PointerGetDatum(groupname));
if (!OidIsValid(groupoid) && !missing_ok) {
ereport(
ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("Target node group \"%s\" does not exist", groupname)));
}
#ifdef ENABLE_MULTIPLE_NODES
UpdateGrpidInCache(groupname, groupoid, version);
#endif
return groupoid;
}
/*
* get_pgxc_groupkind
* Obtain PGXC groupkind for given group name
*/
char get_pgxc_groupkind(Oid group_oid)
{
HeapTuple tuple;
bool isNull = false;
bool is_null = false;
char group_kind;
Datum group_datum;
tuple = SearchSysCache1(PGXCGROUPOID, ObjectIdGetDatum(group_oid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for group %u", group_oid)));
}
group_datum = SysCacheGetAttr(PGXCGROUPOID, tuple, Anum_pgxc_group_is_installation, &isNull);
if (!isNull && DatumGetBool(group_datum)) {
group_kind = PGXC_GROUPKIND_INSTALLATION;
} else {
group_datum = SysCacheGetAttr(PGXCGROUPOID, tuple, Anum_pgxc_group_kind, &is_null);
if (is_null) {
group_kind = PGXC_GROUPKIND_NODEGROUP;
} else if (!isNull && DatumGetChar(group_datum) == PGXC_GROUPKIND_INSTALLATION) {
group_kind = PGXC_GROUPKIND_NODEGROUP;
} else {
group_kind = DatumGetChar(group_datum);
}
}
ReleaseSysCache(tuple);
return group_kind;
}
/*
* get_pgxc_groupmembers
* Obtain PGXC Group members for given group Oid
* Return number of members and their list
*
* Member list is returned as a palloc'd array
*/
int get_pgxc_groupmembers(Oid groupid, Oid** members)
{
HeapTuple tuple;
int nmembers;
oidvector* group_members = NULL;
Oid* nodes = NULL;
bool isNull = true;
Relation rel;
tuple = SearchSysCache1(PGXCGROUPOID, ObjectIdGetDatum(groupid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for group %u", groupid)));
}
/* Get the raw data which contain group memeber's columns */
rel = relation_open(PgxcGroupRelationId, NoLock);
Datum group_member_datum = heap_getattr(tuple, Anum_pgxc_group_members, rel->rd_att, &isNull);
/* if the raw value of group member is null, then report error */
if (isNull) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("null group_members for tuple %u", HeapTupleGetOid(tuple))));
}
group_members = (oidvector*)PG_DETOAST_DATUM(group_member_datum);
nmembers = group_members->dim1;
nodes = group_members->values;
/* The argument members maybe NULL if the caller only get nmembers */
if (members != NULL) {
*members = (Oid*)palloc0(nmembers * sizeof(Oid));
for (int i = 0; i < nmembers; i++) {
(*members)[i] = PgxcNodeGetPrimaryDNFromMatric(nodes[i]);
}
}
if (group_members != (oidvector*)DatumGetPointer(group_member_datum)) {
pfree_ext(group_members);
}
relation_close(rel, NoLock);
ReleaseSysCache(tuple);
return nmembers;
}
/*
* Description: Get group redistribution status for a given group oid.
*
* Parameters:
* @group oid
* Return: a char stand for group redistribution status
*/
char get_pgxc_group_redistributionstatus(Oid groupid)
{
HeapTuple tuple;
Form_pgxc_group groupForm;
char result;
tuple = SearchSysCache1(PGXCGROUPOID, ObjectIdGetDatum(groupid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for group %u", groupid)));
}
groupForm = (Form_pgxc_group)GETSTRUCT(tuple);
result = groupForm->in_redistribution;
Assert(result != '\0');
ReleaseSysCache(tuple);
return result;
}
/*
* get_pgxc_groupmembers_redist
* Obtain PGXC Group members for given group Oid
* Return number of members and their list
*
* Member list is returned as a palloc'd array
* The function think of NodeGroup in redistribution.
* If the NodeGroup is redistributing for adding new nodes,
* we need to return the members including new nodes;
* but if the NodeGroup is redistribuing for deleting nodes,
* we return the original members.
*/
int get_pgxc_groupmembers_redist(Oid groupid, Oid** members)
{
HeapTuple tuple;
int nmembers;
oidvector* group_members = NULL;
Oid* nodes = NULL;
bool isNull = true;
Relation rel;
Datum group_member_datum;
if (members != NULL) {
*members = NULL;
}
tuple = SearchSysCache1(PGXCGROUPOID, ObjectIdGetDatum(groupid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for group %u", groupid)));
}
/* Get the raw data which contain group memeber's columns */
rel = relation_open(PgxcGroupRelationId, NoLock);
group_member_datum = heap_getattr(tuple, Anum_pgxc_group_members, rel->rd_att, &isNull);
/* if the raw value of group member is null, then report error */
if (isNull) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("null group_members for tuple %u", HeapTupleGetOid(tuple))));
}
group_members = (oidvector*)PG_DETOAST_DATUM(group_member_datum);
nmembers = group_members->dim1;
nodes = group_members->values;
if (in_logic_cluster()) {
Form_pgxc_group groupForm = (Form_pgxc_group)GETSTRUCT(tuple);
if (groupForm->in_redistribution == PGXC_REDISTRIBUTION_SRC_GROUP) {
int dst_nmembers = 0;
Oid* dst_members = NULL;
Oid dst_group = PgxcGroupGetRedistDestGroupOid();
if (OidIsValid(dst_group)) {
dst_nmembers = get_pgxc_groupmembers(dst_group, &dst_members);
/* judge whether the node group is expanding. */
if (dst_nmembers > nmembers) {
nmembers = dst_nmembers;
if (members != NULL) {
*members = dst_members;
}
} else {
pfree_ext(dst_members);
}
}
}
}
/* The argument members maybe NULL if the caller only get nmembers */
if (members && *members == NULL) {
*members = (Oid*)palloc0(nmembers * sizeof(Oid));
for (int i = 0; i < nmembers; i++) {
(*members)[i] = nodes[i];
}
}
if (group_members != (oidvector*)DatumGetPointer(group_member_datum)) {
pfree_ext(group_members);
}
relation_close(rel, NoLock);
ReleaseSysCache(tuple);
return nmembers;
}
DistributeBy* getTableDistribution(Oid srcRelid)
{
HeapTuple tuple;
Form_pgxc_class classForm;
DistributeBy* result = makeNode(DistributeBy);
tuple = SearchSysCache1(PGXCCLASSRELID, ObjectIdGetDatum(srcRelid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed on distribution relation %u", srcRelid)));
}
classForm = (Form_pgxc_class)GETSTRUCT(tuple);
switch (classForm->pclocatortype) {
case LOCATOR_TYPE_HASH: // hash
result->disttype = DISTTYPE_HASH;
break;
case LOCATOR_TYPE_REPLICATED: // replication
result->disttype = DISTTYPE_REPLICATION;
break;
case LOCATOR_TYPE_MODULO: // module
result->disttype = DISTTYPE_MODULO;
break;
case LOCATOR_TYPE_RROBIN: // round robin
result->disttype = DISTTYPE_ROUNDROBIN;
break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized distribution option found in source like table")));
break;
}
for (int i = 0; i < classForm->pcattnum.dim1; i++) {
result->colname = lappend(result->colname, makeString(get_attname(srcRelid, classForm->pcattnum.values[i])));
}
ReleaseSysCache(tuple);
return result;
}
DistributeBy* getTableHBucketDistribution(Relation rel)
{
DistributeBy* result = NULL;
if (REALTION_BUCKETKEY_VALID(rel)) {
if (!RELATION_HAS_BUCKET(rel)) {
Assert(false);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized distribution key found in source like table")));
}
result = makeNode(DistributeBy);
result->disttype = DISTTYPE_HASH;
for (int i = 0; i < rel->rd_bucketkey->bucketKey->ndim; i++) {
result->colname = lappend(result->colname,
makeString(get_attname(rel->rd_id, rel->rd_bucketkey->bucketKey->values[i])));
}
}
return result;
}
/*
* get_pgxc_classnodes
* Obtain PGXC class Datanode list for given relation Oid
* Return number of Datanodes and their list
*
* Node list is returned as a palloc'd array
*/
int get_pgxc_classnodes(Oid tableid, Oid** nodes)
{
HeapTuple tuple;
int numnodes;
Relation relation;
oidvector* xc_node = NULL;
bool isNull = false;
relation = heap_open(PgxcClassRelationId, AccessShareLock);
tuple = SearchSysCache1(PGXCCLASSRELID, ObjectIdGetDatum(tableid));
if (!HeapTupleIsValid(tuple)) {
if (!is_sys_table(tableid)) {
Relation rel = relation_open(tableid, NoLock);
StringInfo relName = makeStringInfo();
appendStringInfo(relName, "%s", RelationGetRelationName(rel));
relation_close(rel, NoLock);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("The relation \"%s\" has no distribute type.", relName->data)));
} else {
ereport(
ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", tableid)));
}
}
/*
* After cluster resizeing, the node group is dropped, but the temp table's info in pg_class and
* pgxc_class still exists, so we should do check here.
*/
(void)checkGroup(tableid, false);
Datum xc_node_datum = heap_getattr(tuple, Anum_pgxc_class_nodes, RelationGetDescr(relation), &isNull);
if (isNull) {
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("Can't get nodeoid for relation %s", RelationGetRelationName(relation))));
}
xc_node = (oidvector*)PG_DETOAST_DATUM(xc_node_datum);
numnodes = (int)xc_node->dim1;
if (nodes != NULL) {
errno_t rc = EOK;
*nodes = (Oid*)palloc(numnodes * sizeof(Oid));
rc = memcpy_s(*nodes, numnodes * sizeof(Oid), xc_node->values, numnodes * sizeof(Oid));
securec_check(rc, "\0", "\0");
}
if (xc_node != (oidvector*)DatumGetPointer(xc_node_datum)) {
pfree_ext(xc_node);
}
ReleaseSysCache(tuple);
heap_close(relation, AccessShareLock);
return numnodes;
}
/*
* get_pgxc_class_groupoid
* Obtain PGXC class groupoid by tableoid
*/
Oid get_pgxc_class_groupoid(Oid tableoid)
{
HeapTuple tuple;
Relation relation;
bool isNull = false;
char* groupname = NULL;
#ifdef ENABLE_MULTIPLE_NODES
Oid groupoid = GetCachedGroupid(tableoid);
if (groupoid != InvalidOid)
return groupoid;
#else
Oid groupoid = InvalidOid;
#endif
relation = heap_open(PgxcClassRelationId, AccessShareLock);
tuple = SearchSysCache1(PGXCCLASSRELID, ObjectIdGetDatum(tableoid));
if (!HeapTupleIsValid(tuple)) {
if (!is_sys_table(tableoid)) {
Relation rel = relation_open(tableoid, NoLock);
StringInfo relName = makeStringInfo();
appendStringInfo(relName, "%s", RelationGetRelationName(rel));
relation_close(rel, NoLock);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("The relation \"%s\" has no distribute type.", relName->data)));
} else {
/* system tables are deemed to be under installation group */
groupname = PgxcGroupGetInstallationGroup();
if (groupname == NULL) {
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("There is no installation group for system catalogs!")));
}
isNull = false;
}
} else {
groupname =
NameStr(*(DatumGetName(heap_getattr(tuple, Anum_pgxc_class_pgroup, RelationGetDescr(relation), &isNull))));
}
/* We should have table created in nodegroup */
Assert(groupname != NULL && !isNull);
groupoid = get_pgxc_groupoid(groupname);
if (HeapTupleIsValid(tuple)) {
ReleaseSysCache(tuple);
}
#ifdef ENABLE_MULTIPLE_NODES
CacheGroupId(tableoid, groupoid);
#endif
heap_close(relation, AccessShareLock);
return groupoid;
}
/*
* @Description : Check curent table in pgxc_class
* @in tableoid - table oid
* @return - bool
*/
bool is_pgxc_class_table(Oid tableoid)
{
HeapTuple tuple;
Relation relation;
bool result = true;
relation = heap_open(PgxcClassRelationId, AccessShareLock);
tuple = SearchSysCache1(PGXCCLASSRELID, ObjectIdGetDatum(tableoid));
if (!HeapTupleIsValid(tuple)) {
result = false;
} else {
result = true;
ReleaseSysCache(tuple);
}
heap_close(relation, AccessShareLock);
return result;
}
Oid get_resource_pool_oid(const char* poolname)
{
return GetSysCacheOid1(PGXCRESOURCEPOOLNAME, PointerGetDatum(poolname));
}
char* get_resource_pool_name(Oid poolid)
{
HeapTuple tuple;
Form_pg_resource_pool poolForm;
char* result = NULL;
tuple = SearchSysCache1(PGXCRESOURCEPOOLOID, ObjectIdGetDatum(poolid));
if (!HeapTupleIsValid(tuple)) {
return NULL;
}
poolForm = (Form_pg_resource_pool)GETSTRUCT(tuple);
result = pstrdup(NameStr(poolForm->respool_name));
ReleaseSysCache(tuple);
return result;
}
Form_pg_resource_pool get_resource_pool_param(Oid poolid, Form_pg_resource_pool rp)
{
HeapTuple tuple;
Form_pg_resource_pool poolForm;
bool isnull = false;
Oid parentid = InvalidOid;
int io_limits_value = 0;
char* io_priority_value = NULL;
char* node_group = NULL;
bool is_foreign = false;
int nRet = 0;
tuple = SearchSysCache1(PGXCRESOURCEPOOLOID, ObjectIdGetDatum(poolid));
if (!HeapTupleIsValid(tuple)) {
return NULL;
}
poolForm = (Form_pg_resource_pool)GETSTRUCT(tuple);
Datum ParentDatum = SysCacheGetAttr(PGXCRESOURCEPOOLOID, tuple, Anum_pg_resource_pool_parent, &isnull);
if (!isnull) {
parentid = DatumGetObjectId(ParentDatum);
}
Datum IOLimitsDatum = SysCacheGetAttr(PGXCRESOURCEPOOLOID, tuple, Anum_pg_resource_pool_iops_limits, &isnull);
if (!isnull) {
io_limits_value = DatumGetInt32(IOLimitsDatum);
}
Datum IOPrioityDatum = SysCacheGetAttr(PGXCRESOURCEPOOLOID, tuple, Anum_pg_resource_pool_io_priority, &isnull);
if (!isnull) {
io_priority_value = DatumGetCString(DirectFunctionCall1(nameout, IOPrioityDatum));
}
Datum NodeGroupDatum = SysCacheGetAttr(PGXCRESOURCEPOOLOID, tuple, Anum_pg_resource_pool_nodegroup, &isnull);
if (!isnull) {
node_group = DatumGetCString(DirectFunctionCall1(nameout, NodeGroupDatum));
}
Datum IsForeignDatum = SysCacheGetAttr(PGXCRESOURCEPOOLOID, tuple, Anum_pg_resource_pool_is_foreign, &isnull);
if (!isnull) {
is_foreign = DatumGetBool(IsForeignDatum);
}
if (rp) {
nRet = memcpy_s(NameStr(rp->respool_name), NAMEDATALEN, NameStr(poolForm->respool_name), NAMEDATALEN);
securec_check(nRet, "\0", "\0");
rp->mem_percent = poolForm->mem_percent;
rp->active_statements = poolForm->active_statements;
rp->max_dop = poolForm->max_dop;
rp->cpu_affinity = poolForm->cpu_affinity;
nRet = memcpy_s(NameStr(rp->control_group), NAMEDATALEN, NameStr(poolForm->control_group), NAMEDATALEN);
securec_check(nRet, "\0", "\0");
nRet = memcpy_s(NameStr(rp->memory_limit), NAMEDATALEN, NameStr(poolForm->memory_limit), NAMEDATALEN);
securec_check(nRet, "\0", "\0");
rp->parentid = parentid;
rp->iops_limits = io_limits_value;
if (io_priority_value != NULL) {
errno_t errval = strncpy_s(NameStr(rp->io_priority), NAMEDATALEN, io_priority_value, NAMEDATALEN - 1);
securec_check_errval(errval,, LOG);
pfree_ext(io_priority_value);
} else {
errno_t errval = strncpy_s(NameStr(rp->io_priority), NAMEDATALEN, "None", NAMEDATALEN - 1);
securec_check_errval(errval,, LOG);
}
if (node_group != NULL) {
errno_t errval = strncpy_s(NameStr(rp->nodegroup), NAMEDATALEN, node_group, NAMEDATALEN - 1);
securec_check_errval(errval,, LOG);
pfree_ext(node_group);
} else {
errno_t errval = strncpy_s(NameStr(rp->nodegroup), NAMEDATALEN, "installation", NAMEDATALEN - 1);
securec_check_errval(errval,, LOG);
}
rp->is_foreign = is_foreign;
}
ReleaseSysCache(tuple);
return rp;
}
char* get_resource_pool_ngname(Oid poolid)
{
HeapTuple tuple;
char* result = NULL;
Relation rel;
bool isNull = false;
Datum groupanme_datum;
rel = heap_open(ResourcePoolRelationId, AccessShareLock);
if (!rel) {
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("can not open pg_resource_pool")));
}
tuple = SearchSysCache1(PGXCRESOURCEPOOLOID, ObjectIdGetDatum(poolid));
if (!HeapTupleIsValid(tuple)) {
return NULL;
}
groupanme_datum = heap_getattr(tuple, Anum_pg_resource_pool_nodegroup, RelationGetDescr(rel), &isNull);
if (!isNull) {
result = (char*)pstrdup((const char*)DatumGetCString(groupanme_datum));
} else {
result = (char*)pstrdup(DEFAULT_NODE_GROUP);
}
ReleaseSysCache(tuple);
heap_close(rel, AccessShareLock);
return result;
}
/*
* is_resource_pool_foreign
* Determine if resource pool is foreign one
*/
bool is_resource_pool_foreign(Oid poolid)
{
HeapTuple tuple;
bool result = false;
Relation rel;
bool isNull = false;
Datum datum;
rel = heap_open(ResourcePoolRelationId, AccessShareLock);
if (!rel) {
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("can not open pg_resource_pool")));
}
tuple = SearchSysCache1(PGXCRESOURCEPOOLOID, ObjectIdGetDatum(poolid));
if (!HeapTupleIsValid(tuple)) {
ereport(
ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for resource pool %u", poolid)));
}
datum = heap_getattr(tuple, Anum_pg_resource_pool_is_foreign, RelationGetDescr(rel), &isNull);
if (!isNull) {
result = DatumGetBool(datum);
} else {
result = false;
}
ReleaseSysCache(tuple);
heap_close(rel, AccessShareLock);
return result;
}
Oid get_workload_group_oid(const char* groupname)
{
return GetSysCacheOid1(PGXCWORKLOADGROUPNAME, PointerGetDatum(groupname));
}
char* get_workload_group_name(Oid groupid)
{
HeapTuple tuple;
Form_pg_workload_group groupForm;
char* result = NULL;
tuple = SearchSysCache1(PGXCWORKLOADGROUPOID, ObjectIdGetDatum(groupid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for workload group %u", groupid)));
}
groupForm = (Form_pg_workload_group)GETSTRUCT(tuple);
result = pstrdup(NameStr(groupForm->workload_gpname));
ReleaseSysCache(tuple);
return result;
}
Oid get_application_mapping_oid(const char* appname)
{
return GetSysCacheOid1(PGXCAPPWGMAPPINGNAME, PointerGetDatum(appname));
}
char* get_application_mapping_name(Oid appoid)
{
HeapTuple tuple;
Form_pg_app_workloadgroup_mapping mappingForm;
char* result = NULL;
tuple = SearchSysCache1(PGXCAPPWGMAPPINGOID, ObjectIdGetDatum(appoid));
if (!HeapTupleIsValid(tuple)) {
ereport(
ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for application %u", appoid)));
}
mappingForm = (Form_pg_app_workloadgroup_mapping)GETSTRUCT(tuple);
result = pstrdup(NameStr(mappingForm->appname));
ReleaseSysCache(tuple);
return result;
}
#endif
/*
* get_typavgwidth
*
* Given a type OID and a typmod value (pass -1 if typmod is unknown),
* estimate the average width of values of the type. This is used by
* the planner, which doesn't require absolutely correct results;
* it's OK (and expected) to guess if we don't know for sure.
*/
int32 get_typavgwidth(Oid typid, int32 typmod)
{
int typlen = get_typlen(typid);
int32 maxwidth;
/*
* Easy if it's a fixed-width type
*/
if (typlen > 0) {
return typlen;
}
/*
* type_maximum_size knows the encoding of typmod for some datatypes;
* don't duplicate that knowledge here.
*/
maxwidth = type_maximum_size(typid, typmod);
if (maxwidth > 0) {
/*
* For BPCHAR, the max width is also the only width. Otherwise we
* need to guess about the typical data width given the max. A sliding
* scale for percentage of max width seems reasonable.
*/
if (typid == BPCHAROID) {
return maxwidth;
}
if (maxwidth <= 32) {
return maxwidth; /* assume full width */
}
if (maxwidth < 1000) {
return 32 + (maxwidth - 32) / 2; /* assume 50% */
}
/*
* Beyond 1000, assume we're looking at something like
* "varchar(10000)" where the limit isn't actually reached often, and
* use a fixed estimate.
*/
return 32 + (1000 - 32) / 2;
}
/*
* Ooops, we have no idea ... wild guess time.
*/
return 32;
}
/*
* get_typtype
*
* Given the type OID, find if it is a basic type, a complex type, etc.
* It returns the null char if the cache lookup fails...
*/
char get_typtype(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
char result;
result = typtup->typtype;
ReleaseSysCache(tp);
return result;
} else {
return '\0';
}
}
/*
* type_is_rowtype
*
* Convenience function to determine whether a type OID represents
* a "rowtype" type --- either RECORD or a named composite type.
*/
bool type_is_rowtype(Oid typid)
{
return (typid == RECORDOID || get_typtype(typid) == TYPTYPE_COMPOSITE);
}
/*
* type_is_enum
* Returns true if the given type is an enum type.
*/
bool type_is_enum(Oid typid)
{
return (get_typtype(typid) == TYPTYPE_ENUM);
}
/*
* type_is_range
* Returns true if the given type is a range type.
*/
bool type_is_range(Oid typid)
{
return (get_typtype(typid) == TYPTYPE_RANGE);
}
/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
* Throws error on failure.
*/
void get_type_category_preferred(Oid typid, char* typcategory, bool* typispreferred)
{
HeapTuple tp;
Form_pg_type typtup;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typtup = (Form_pg_type)GETSTRUCT(tp);
*typcategory = typtup->typcategory;
*typispreferred = typtup->typispreferred;
ReleaseSysCache(tp);
}
/*
* get_typ_typrelid
*
* Given the type OID, get the typrelid (InvalidOid if not a complex
* type).
*/
Oid get_typ_typrelid(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
Oid result;
result = typtup->typrelid;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_element_type
*
* Given the type OID, get the typelem (InvalidOid if not an array type).
*
* NB: this only considers varlena arrays to be true arrays; InvalidOid is
* returned if the input is a fixed-length array type.
*/
Oid get_element_type(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
Oid result;
if (typtup->typlen == -1) {
result = typtup->typelem;
} else {
result = InvalidOid;
}
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_array_type
*
* Given the type OID, get the corresponding "true" array type.
* Returns InvalidOid if no array type can be found.
*/
Oid get_array_type(Oid typid)
{
HeapTuple tp;
Oid result = InvalidOid;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
result = ((Form_pg_type)GETSTRUCT(tp))->typarray;
ReleaseSysCache(tp);
}
return result;
}
/*
* get_base_element_type
* Given the type OID, get the typelem, looking "through" any domain
* to its underlying array type.
*
* This is equivalent to get_element_type(getBaseType(typid)), but avoids
* an extra cache lookup. Note that it fails to provide any information
* about the typmod of the array.
*/
Oid get_base_element_type(Oid typid)
{
/*
* We loop to find the bottom base type in a stack of domains.
*/
for (;;) {
HeapTuple tup;
Form_pg_type typTup;
tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tup)) {
break;
}
typTup = (Form_pg_type)GETSTRUCT(tup);
if (typTup->typtype != TYPTYPE_DOMAIN) {
/* Not a domain, so stop descending */
Oid result;
/* This test must match get_element_type */
if (typTup->typlen == -1) {
result = typTup->typelem;
} else {
result = InvalidOid;
}
ReleaseSysCache(tup);
return result;
}
typid = typTup->typbasetype;
ReleaseSysCache(tup);
}
/* Like get_element_type, silently return InvalidOid for bogus input */
return InvalidOid;
}
/*
* getTypeInputInfo
*
* Get info needed for converting values of a type to internal form
*/
void getTypeInputInfo(Oid type, Oid* typInput, Oid* typIOParam)
{
HeapTuple typeTuple;
Form_pg_type pt;
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", type)));
}
pt = (Form_pg_type)GETSTRUCT(typeTuple);
if (!pt->typisdefined) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s is only a shell", format_type_be(type))));
}
if (!OidIsValid(pt->typinput)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("no input function available for type %s", format_type_be(type))));
}
*typInput = pt->typinput;
*typIOParam = getTypeIOParam(typeTuple);
ReleaseSysCache(typeTuple);
}
/*
* getTypeOutputInfo
*
* Get info needed for printing values of a type
*/
void getTypeOutputInfo(Oid type, Oid* typOutput, bool* typIsVarlena)
{
HeapTuple typeTuple;
Form_pg_type pt;
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", type)));
}
pt = (Form_pg_type)GETSTRUCT(typeTuple);
if (!pt->typisdefined) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s is only a shell", format_type_be(type))));
}
if (!OidIsValid(pt->typoutput)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("no output function available for type %s", format_type_be(type))));
}
*typOutput = pt->typoutput;
*typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
ReleaseSysCache(typeTuple);
}
/*
* getTypeBinaryInputInfo
*
* Get info needed for binary input of values of a type
*/
void getTypeBinaryInputInfo(Oid type, Oid* typReceive, Oid* typIOParam)
{
HeapTuple typeTuple;
Form_pg_type pt;
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", type)));
}
pt = (Form_pg_type)GETSTRUCT(typeTuple);
if (!pt->typisdefined) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s is only a shell", format_type_be(type))));
}
if (!OidIsValid(pt->typreceive)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("no binary input function available for type %s", format_type_be(type))));
}
*typReceive = pt->typreceive;
*typIOParam = getTypeIOParam(typeTuple);
ReleaseSysCache(typeTuple);
}
/*
* getTypeBinaryOutputInfo
*
* Get info needed for binary output of values of a type
*/
void getTypeBinaryOutputInfo(Oid type, Oid* typSend, bool* typIsVarlena)
{
HeapTuple typeTuple;
Form_pg_type pt;
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", type)));
}
pt = (Form_pg_type)GETSTRUCT(typeTuple);
if (!pt->typisdefined) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s is only a shell", format_type_be(type))));
}
if (!OidIsValid(pt->typsend)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("no binary output function available for type %s", format_type_be(type))));
}
*typSend = pt->typsend;
*typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
ReleaseSysCache(typeTuple);
}
/*
* get_typmodin
*
* Given the type OID, return the type's typmodin procedure, if any.
*/
Oid get_typmodin(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
Oid result;
result = typtup->typmodin;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
#ifdef NOT_USED
/*
* get_typmodout
*
* Given the type OID, return the type's typmodout procedure, if any.
*/
Oid get_typmodout(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
Oid result;
result = typtup->typmodout;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
#endif /* NOT_USED */
/*
* get_typcollation
*
* Given the type OID, return the type's typcollation attribute.
*/
Oid get_typcollation(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
Oid result;
result = typtup->typcollation;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* type_is_collatable
*
* Return whether the type cares about collations
*/
bool type_is_collatable(Oid typid)
{
return OidIsValid(get_typcollation(typid));
}
/*
* get_attavgwidth
*
* Given the table and attribute number of a column, get the average
* width of entries in the column. Return zero if no data available.
*
* Currently this is only consulted for individual tables, not for inheritance
* trees, so we don't need an "inh" parameter.
*
* Calling a hook at this point looks somewhat strange, but is required
* because the optimizer calls this function without any other way for
* plug-ins to control the result.
*/
int32 get_attavgwidth(Oid relid, AttrNumber attnum, bool ispartition)
{
HeapTuple tp;
int32 stawidth;
char stakind = ispartition ? STARELKIND_PARTITION : STARELKIND_CLASS;
if (u_sess->attr.attr_common.upgrade_mode != 0) {
return 0;
}
tp = SearchSysCache4(
STATRELKINDATTINH, ObjectIdGetDatum(relid), CharGetDatum(stakind), Int16GetDatum(attnum), BoolGetDatum(false));
if (HeapTupleIsValid(tp)) {
stawidth = ((Form_pg_statistic)GETSTRUCT(tp))->stawidth;
ReleaseSysCache(tp);
if (stawidth > 0) {
return stawidth;
}
}
return 0;
}
/*
* For global stat: get stadndistinct from pg_statistic.
*/
double get_attstadndistinct(HeapTuple statstuple)
{
Datum val;
bool isnull = false;
if (u_sess->attr.attr_common.upgrade_mode != 0) {
return 0;
}
val = SysCacheGetAttr(STATRELKINDATTINH, statstuple, Anum_pg_statistic_stadndistinct, &isnull);
if (isnull) {
return 0;
} else {
return DatumGetFloat4(val);
}
}
/*
* get_attstatsslot
*
* Extract the contents of a "slot" of a pg_statistic tuple.
* Returns TRUE if requested slot type was found, else FALSE.
*
* Unlike other routines in this file, this takes a pointer to an
* already-looked-up tuple in the pg_statistic cache. We do this since
* most callers will want to extract more than one value from the cache
* entry, and we don't want to repeat the cache lookup unnecessarily.
* Also, this API allows this routine to be used with statistics tuples
* that have been provided by a stats hook and didn't really come from
* pg_statistic.
*
* statstuple: pg_statistics tuple to be examined.
* atttype: type OID of slot's stavalues (can be InvalidOid if values == NULL).
* atttypmod: typmod of slot's stavalues (can be 0 if values == NULL).
* reqkind: STAKIND code for desired statistics slot kind.
* reqop: STAOP value wanted, or InvalidOid if don't care.
* actualop: if not NULL, *actualop receives the actual STAOP value.
* values, nvalues: if not NULL, the slot's stavalues are extracted.
* numbers, nnumbers: if not NULL, the slot's stanumbers are extracted.
*
* If assigned, values and numbers are set to point to palloc'd arrays.
* If the stavalues datatype is pass-by-reference, the values referenced by
* the values array are themselves palloc'd. The palloc'd stuff can be
* freed by calling free_attstatsslot.
*
* Note: at present, atttype/atttypmod aren't actually used here at all.
* But the caller must have the correct (or at least binary-compatible)
* type ID to pass to free_attstatsslot later.
*/
bool get_attstatsslot(HeapTuple statstuple, Oid atttype, int32 atttypmod, int reqkind, Oid reqop, Oid* actualop,
Datum** values, int* nvalues, float4** numbers, int* nnumbers)
{
Form_pg_statistic stats = (Form_pg_statistic)GETSTRUCT(statstuple);
int i, j;
Datum val;
bool isnull = false;
ArrayType* statarray = NULL;
Oid arrayelemtype;
int narrayelem;
HeapTuple typeTuple;
Form_pg_type typeForm;
if (u_sess->attr.attr_common.upgrade_mode != 0) {
return false;
}
for (i = 0; i < STATISTIC_NUM_SLOTS; ++i) {
if ((&stats->stakind1)[i] == reqkind && (reqop == InvalidOid || (&stats->staop1)[i] == reqop)) {
break;
}
}
if (i >= STATISTIC_NUM_SLOTS) {
return false; /* not there */
}
if (actualop != NULL) {
*actualop = (&stats->staop1)[i];
}
if (values != NULL) {
val = SysCacheGetAttr(STATRELKINDATTINH, statstuple, Anum_pg_statistic_stavalues1 + i, &isnull);
if (isnull) {
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("stavalues is null")));
}
statarray = DatumGetArrayTypeP(val);
/*
* Need to get info about the array element type. We look at the
* actual element type embedded in the array, which might be only
* binary-compatible with the passed-in atttype. The info we extract
* here should be the same either way, but deconstruct_array is picky
* about having an exact type OID match.
*/
arrayelemtype = ARR_ELEMTYPE(statarray);
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", arrayelemtype)));
}
typeForm = (Form_pg_type)GETSTRUCT(typeTuple);
/* Deconstruct array into Datum elements; NULLs not expected */
deconstruct_array(
statarray, arrayelemtype, typeForm->typlen, typeForm->typbyval, typeForm->typalign, values, NULL, nvalues);
/*
* If the element type is pass-by-reference, we now have a bunch of
* Datums that are pointers into the syscache value. Copy them to
* avoid problems if syscache decides to drop the entry.
*/
if (!typeForm->typbyval) {
for (j = 0; j < *nvalues; j++) {
(*values)[j] = datumCopy((*values)[j], typeForm->typbyval, typeForm->typlen);
}
}
ReleaseSysCache(typeTuple);
/*
* Free statarray if it's a detoasted copy.
*/
if ((Pointer)statarray != DatumGetPointer(val)) {
pfree_ext(statarray);
}
}
if (numbers != NULL) {
val = SysCacheGetAttr(STATRELKINDATTINH, statstuple, Anum_pg_statistic_stanumbers1 + i, &isnull);
if (isnull) {
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("stanumbers is null")));
}
statarray = DatumGetArrayTypeP(val);
/*
* We expect the array to be a 1-D float4 array; verify that. We don't
* need to use deconstruct_array() since the array data is just going
* to look like a C array of float4 values.
*/
narrayelem = ARR_DIMS(statarray)[0];
if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 || ARR_HASNULL(statarray) ||
ARR_ELEMTYPE(statarray) != FLOAT4OID) {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("stanumbers is not a 1-D float4 array")));
}
*numbers = (float4*)palloc(narrayelem * sizeof(float4));
int rc = memcpy_s(*numbers, narrayelem * sizeof(float4), ARR_DATA_PTR(statarray), narrayelem * sizeof(float4));
securec_check(rc, "\0", "\0");
*nnumbers = narrayelem;
/*
* Free statarray if it's a detoasted copy.
*/
if ((Pointer)statarray != DatumGetPointer(val)) {
pfree_ext(statarray);
}
}
return true;
}
bool get_attmultistatsslot(HeapTuple statstuple, Oid atttype, int32 atttypmod, int reqkind, Oid reqop, Oid* actualop,
Datum** values, int* nvalues, float4** numbers, int* nnumbers, bool** nulls)
{
Form_pg_statistic_ext stats = (Form_pg_statistic_ext)GETSTRUCT(statstuple);
Datum val;
bool isnull = false;
ArrayType* statarray = NULL;
Oid arrayelemtype;
int narrayelem;
HeapTuple typeTuple;
Form_pg_type typeForm;
int slot_index = 0;
for (slot_index = 0; slot_index < STATISTIC_NUM_SLOTS; ++slot_index) {
if ((&stats->stakind1)[slot_index] == reqkind && (reqop == InvalidOid ||
(&stats->staop1)[slot_index] == reqop)) {
break;
}
}
if (slot_index >= STATISTIC_NUM_SLOTS) {
return false; /* not there */
}
if (actualop != NULL) {
*actualop = (&stats->staop1)[slot_index];
}
if (values != NULL) {
val = SysCacheGetAttr(STATRELKINDKEYINH, statstuple, Anum_pg_statistic_ext_stavalues1 + slot_index, &isnull);
if (isnull) {
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("stavalues is null")));
}
statarray = DatumGetArrayTypeP(val);
/*
* Need to get info about the array element type. We look at the
* actual element type embedded in the array, which might be only
* binary-compatible with the passed-in atttype. The info we extract
* here should be the same either way, but deconstruct_array is picky
* about having an exact type OID match.
*/
arrayelemtype = ARR_ELEMTYPE(statarray);
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
if (!HeapTupleIsValid(typeTuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", arrayelemtype)));
}
typeForm = (Form_pg_type)GETSTRUCT(typeTuple);
/* Deconstruct array into Datum elements; NULLs not expected */
deconstruct_array(
statarray, arrayelemtype, typeForm->typlen, typeForm->typbyval, typeForm->typalign, values, NULL, nvalues);
Datum* all_attr_values = NULL;
bool* all_attr_nulls = NULL;
if (reqkind == STATISTIC_KIND_MCV || reqkind == STATISTIC_KIND_NULL_MCV) {
int attr_num = *nvalues;
for (int i = 0; i < attr_num; ++i) {
ArrayType* attr_value_array = DatumGetArrayTypeP((*values)[i]);
Oid attr_type = ARR_ELEMTYPE(attr_value_array);
HeapTuple attr_type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(attr_type));
if (!HeapTupleIsValid(attr_type_tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", attr_type)));
}
Form_pg_type attr_type_form = (Form_pg_type)GETSTRUCT(attr_type_tuple);
Datum* attr_values = NULL;
bool* attr_nulls = NULL;
int attr_nvalues = 0;
deconstruct_array(attr_value_array,
attr_type,
attr_type_form->typlen,
attr_type_form->typbyval,
attr_type_form->typalign,
&attr_values,
&attr_nulls,
&attr_nvalues);
if (all_attr_values == NULL) {
all_attr_values = (Datum*)palloc0(sizeof(Datum) * attr_nvalues * attr_num);
if (reqkind == STATISTIC_KIND_NULL_MCV)
all_attr_nulls = (bool*)palloc0(sizeof(bool) * attr_nvalues * attr_num);
*nvalues = attr_nvalues * attr_num;
}
for (int j = 0; j < attr_nvalues; ++j) {
if (all_attr_nulls != NULL) {
all_attr_nulls[attr_nvalues * i + j] = attr_nulls[j];
}
if (!attr_nulls[j]) {
all_attr_values[attr_nvalues * i + j] =
attr_type_form->typbyval
? attr_values[j]
: datumCopy(attr_values[j], attr_type_form->typbyval, attr_type_form->typlen);
}
}
ReleaseSysCache(attr_type_tuple);
if ((Pointer)attr_value_array != DatumGetPointer((*values)[i])) {
pfree_ext(attr_value_array);
}
pfree_ext(attr_values);
pfree_ext(attr_nulls);
}
}
/*
* If the element type is pass-by-reference, we now have a bunch of
* Datums that are pointers into the syscache value. Copy them to
* avoid problems if syscache decides to drop the entry.
*/
if (all_attr_values != NULL) {
*values = all_attr_values;
if (nulls != NULL) {
*nulls = all_attr_nulls;
}
}
ReleaseSysCache(typeTuple);
/*
* Free statarray if it's a detoasted copy.
*/
if ((Pointer)statarray != DatumGetPointer(val)) {
pfree_ext(statarray);
}
}
if (numbers != NULL) {
val = SysCacheGetAttr(STATRELKINDKEYINH, statstuple, Anum_pg_statistic_ext_stanumbers1 + slot_index, &isnull);
if (isnull) {
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("stanumbers is null")));
}
statarray = DatumGetArrayTypeP(val);
/*
* We expect the array to be a 1-D float4 array; verify that. We don't
* need to use deconstruct_array() since the array data is just going
* to look like a C array of float4 values.
*/
narrayelem = ARR_DIMS(statarray)[0];
if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 || ARR_HASNULL(statarray) ||
ARR_ELEMTYPE(statarray) != FLOAT4OID) {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("stanumbers is not a 1-D float4 array")));
}
*numbers = (float4*)palloc(narrayelem * sizeof(float4));
int rc = memcpy_s(*numbers, narrayelem * sizeof(float4), ARR_DATA_PTR(statarray), narrayelem * sizeof(float4));
securec_check(rc, "\0", "\0");
*nnumbers = narrayelem;
/*
* Free statarray if it's a detoasted copy.
*/
if ((Pointer)statarray != DatumGetPointer(val)) {
pfree_ext(statarray);
}
}
return true;
}
/*
* free_attstatsslot
* Free data allocated by get_attstatsslot
*
* atttype is the type of the individual values in values[].
* It need be valid only if values != NULL.
*/
void free_attstatsslot(Oid atttype, Datum* values, int nvalues, float4* numbers, int nnumbers)
{
if (values != NULL) {
if (!get_typbyval(atttype)) {
int i;
for (i = 0; i < nvalues; i++) {
pfree(DatumGetPointer(values[i]));
}
}
pfree_ext(values);
}
if (numbers != NULL) {
pfree_ext(numbers);
}
}
/*
* get_namespace_name
* Returns the name of a given namespace
*
* Returns a palloc'd copy of the string, or NULL if no such namespace.
*/
char* get_namespace_name(Oid nspid, bool report_error)
{
HeapTuple tp;
tp = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid));
if (HeapTupleIsValid(tp)) {
Form_pg_namespace nsptup = (Form_pg_namespace)GETSTRUCT(tp);
char* result = NULL;
result = pstrdup(NameStr(nsptup->nspname));
ReleaseSysCache(tp);
return result;
} else {
if (report_error) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for namespace %u", nspid)));
}
return NULL;
}
}
/*
* get_range_subtype
* Returns the subtype of a given range type
*
* Returns InvalidOid if the type is not a range type.
*/
Oid get_range_subtype(Oid rangeOid)
{
HeapTuple tp;
tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
if (HeapTupleIsValid(tp)) {
Form_pg_range rngtup = (Form_pg_range)GETSTRUCT(tp);
Oid result;
result = rngtup->rngsubtype;
ReleaseSysCache(tp);
return result;
} else {
return InvalidOid;
}
}
/*
* get_cfgnamespace
* Returns the namespace of a given cfgid
*
*/
char* get_cfgnamespace(Oid cfgid)
{
HeapTuple tuple;
Form_pg_type typeForm;
char* result = NULL;
tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", cfgid)));
}
typeForm = (Form_pg_type)GETSTRUCT(tuple);
result = pstrdup(get_namespace_name(typeForm->typnamespace, true));
ReleaseSysCache(tuple);
return result;
}
/*
* get_range_subtype
* Returns the namespace of a given typeid
*
*/
char* get_typenamespace(Oid typid)
{
HeapTuple tuple;
Form_pg_type typeForm;
char* result = NULL;
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typid)));
}
typeForm = (Form_pg_type)GETSTRUCT(tuple);
result = pstrdup(get_namespace_name(typeForm->typnamespace, true));
ReleaseSysCache(tuple);
return result;
}
/*
* get_range_subtype
* Returns the namespace of the enumtype corresponding to the given enumtype labelid
*/
char* get_enumtypenamespace(Oid enumlabelid)
{
HeapTuple tuple;
Form_pg_enum en;
Form_pg_type typeForm;
Oid enumtypid = InvalidOid;
char* result = NULL;
tuple = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumlabelid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for enumlabelid %u", enumlabelid)));
}
en = (Form_pg_enum)GETSTRUCT(tuple);
enumtypid = en->enumtypid;
ReleaseSysCache(tuple);
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enumtypid));
if (!HeapTupleIsValid(tuple)) {
ereport(
ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for enumtype %u", enumtypid)));
}
typeForm = (Form_pg_type)GETSTRUCT(tuple);
result = get_namespace_name(typeForm->typnamespace);
ReleaseSysCache(tuple);
return result;
}
Oid get_typeoid(Oid namespaceId, const char* typname)
{
Oid result;
result = GetSysCacheOid2(TYPENAMENSP, PointerGetDatum(typname), ObjectIdGetDatum(namespaceId));
if (OidIsValid(result)) {
return result;
}
/* Not found in path */
return InvalidOid;
}
Oid get_enumlabeloid(Oid enumtypoid, const char* enumlabelname)
{
Oid result;
HeapTuple tup;
tup = SearchSysCache2(ENUMTYPOIDNAME, ObjectIdGetDatum(enumtypoid), CStringGetDatum(enumlabelname));
if (!HeapTupleIsValid(tup)) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input value for enum %s: \"%s\"", format_type_be(enumtypoid), enumlabelname)));
}
result = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
return result;
}
/*
* Returns the operator oid with the given operatorName, operatorNamespace, leftObjectId, rightObjectId
*/
Oid get_operator_oid(const char* operatorName, Oid operatorNamespace, Oid leftObjectId, Oid rightObjectId)
{
HeapTuple tup;
Oid operatorObjectId;
tup = SearchSysCache(OPERNAMENSP,
PointerGetDatum(operatorName),
ObjectIdGetDatum(leftObjectId),
ObjectIdGetDatum(rightObjectId),
ObjectIdGetDatum(operatorNamespace));
if (HeapTupleIsValid(tup)) {
operatorObjectId = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
} else {
operatorObjectId = InvalidOid;
}
return operatorObjectId;
}
/*
* Returns the operator name, operator namespace, left operator type oid, right operator type
* oid of the given operator oid
*/
void get_oper_name_namespace_oprs(Oid operid, char** oprname, char** nspname, Oid* oprleft, Oid* oprright)
{
HeapTuple opertup;
Form_pg_operator operform;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
if (!HeapTupleIsValid(opertup)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for operator %u", operid)));
}
operform = (Form_pg_operator)GETSTRUCT(opertup);
*oprname = pstrdup(NameStr(operform->oprname));
*nspname = get_namespace_name(operform->oprnamespace, true);
*oprleft = operform->oprleft;
*oprright = operform->oprright;
ReleaseSysCache(opertup);
}
Oid get_func_oid(const char* funcname, Oid funcnamespace, Expr* expr)
{
CatCList* catlist = NULL;
int i;
Oid argtypes[FUNC_MAX_ARGS] = {0};
int nargs = 0;
ListCell* l = NULL;
if (expr != NULL && IsA(expr, FuncExpr)) {
foreach (l, ((FuncExpr*)expr)->args) {
Node* arg = (Node*)lfirst(l);
argtypes[nargs] = exprType(arg);
nargs++;
}
}
/* Search syscache by name only */
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple proctup = &catlist->members[i]->tuple;
Form_pg_proc procform = (Form_pg_proc)GETSTRUCT(proctup);
if (OidIsValid(funcnamespace)) {
/* Consider only procs in specified namespace */
if (procform->pronamespace != funcnamespace) {
continue;
}
}
if (expr != NULL && IsA(expr, FuncExpr) && procform->pronargs != list_length(((FuncExpr*)expr)->args)) {
continue;
}
if (expr != NULL && IsA(expr, FuncExpr) && procform->prorettype != ((FuncExpr*)expr)->funcresulttype) {
continue;
}
/*
* Support the same function name, same number of arguments and different argument type
*/
if (expr != NULL && IsA(expr, FuncExpr)) {
int j = 0;
bool matched = true;
for (j = 0; j < nargs; j++) {
if (argtypes[j] != procform->proargtypes.values[j]) {
matched = false;
break;
}
}
if (!matched) {
continue;
}
}
ReleaseSysCacheList(catlist);
return HeapTupleGetOid(proctup);
}
ReleaseSysCacheList(catlist);
return InvalidOid;
}
Oid get_func_oid_ext(const char* funcname, Oid funcnamespace, Oid funcrettype, int funcnargs, Oid* funcargstype)
{
CatCList* catlist = NULL;
int i, j;
/* Search syscache by name only */
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple proctup = &catlist->members[i]->tuple;
Form_pg_proc procform = (Form_pg_proc)GETSTRUCT(proctup);
if (OidIsValid(funcnamespace)) {
/* Consider only procs in specified namespace */
if (procform->pronamespace != funcnamespace) {
continue;
}
}
if (procform->pronargs != funcnargs) {
continue;
}
if (procform->prorettype != funcrettype) {
continue;
}
/*
* Support the same function name, same number of arguments and different argument type
*/
for (j = 0; j < funcnargs; j++) {
if (funcargstype[j] != procform->proargtypes.values[j]) {
break;
}
}
if (j < funcnargs) {
continue;
}
ReleaseSysCacheList(catlist);
return HeapTupleGetOid(proctup);
}
ReleaseSysCacheList(catlist);
return InvalidOid;
}
Oid partid_get_parentid(Oid partid)
{
Form_pg_partition partitionForm;
HeapTuple partitionTup;
Oid relid;
/* Search the partition's relcache entry */
partitionTup = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid));
if (!HeapTupleIsValid(partitionTup)) {
return InvalidOid;
}
partitionForm = (Form_pg_partition)GETSTRUCT(partitionTup);
/* Get the partitioned table's oid */
relid = partitionForm->parentid;
ReleaseSysCache(partitionTup);
return relid;
}
/* function is not strict and not agg */
bool is_not_strict_agg(Oid funcOid)
{
HeapTuple func_tuple;
Form_pg_proc func_form;
func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
if (!HeapTupleIsValid(func_tuple)) {
return false;
}
func_form = (Form_pg_proc)GETSTRUCT(func_tuple);
if (func_form->proisstrict == false && !PROC_IS_AGG(func_form->prokind)) {
ReleaseSysCache(func_tuple);
return true;
}
ReleaseSysCache(func_tuple);
return false;
}