*
* 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
* Portions Copyright (c) 2021, openGauss Contributors
*
* 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_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.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/pgxc_slice.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 "catalog/storage_gtt.h"
#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/partitionkey.h"
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "utils/snapmgr.h"
#include "storage/proc.h"
#include "utils/syscache.h"
#include "access/heapam.h"
#include "utils/typcache.h"
#include "optimizer/streamplan.h"
#include "pgxc/pgxc.h"
#include "utils/acl.h"
#include "streaming/planner.h"
#include "catalog/pgxc_group.h"
#include "catalog/pg_proc_fn.h"
#include "catalog/gs_package.h"
#ifndef ENABLE_MULTIPLE_NODES
#include "optimizer/func_dependency.h"
#endif
#ifdef USE_SPQ
#include "access/sysattr.h"
#include "funcapi.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_trigger.h"
#include "parser/parse_coerce.h"
#endif
* 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.
*/
constexpr int OPERATOR_EQUALS_STRATEGY = 3;
constexpr Oid OPERATOR_EQUALS_OPNO = 96;
int get_op_opfamily_strategy(Oid opno, Oid opfamily)
{
HeapTuple tp;
Form_pg_amop amop_tup;
int result;
if (opno == OPERATOR_EQUALS_OPNO && opfamily == INTEGER_UBTREE_FAM_OID) {
result = OPERATOR_EQUALS_STRATEGY;
return 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;
*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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
if (!OID_IS_BTREE(aform->amopmethod)) {
continue;
}
if (aform->amopstrategy == BTLessStrategyNumber || aform->amopstrategy == BTGreaterStrategyNumber) {
if (aform->amoplefttype == aform->amoprighttype) {
*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;
if (get_ordering_op_properties(opno, &opfamily, &opcintype, &strategy)) {
*cmpfunc = get_opfamily_proc(opfamily, opcintype, opcintype, BTORDER_PROC);
if (!OidIsValid(*cmpfunc)) {
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;
}
*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;
if (get_ordering_op_properties(opno, &opfamily, &opcintype, &strategy)) {
*sortfunc = get_opfamily_proc(opfamily, opcintype, opcintype, BTSORTSUPPORT_PROC);
if (OidIsValid(*sortfunc)) {
*issupport = true;
} else {
*sortfunc = get_opfamily_proc(opfamily, opcintype, opcintype, BTORDER_PROC);
if (!OidIsValid(*sortfunc)) {
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;
}
*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;
if (get_ordering_op_properties(opno, &opfamily, &opcintype, &strategy)) {
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
if (!OID_IS_BTREE(aform->amopmethod)) {
continue;
}
if (aform->amopstrategy == BTEqualStrategyNumber) {
Oid typid;
typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
result = get_opfamily_member(aform->amopfamily, typid, typid, BTLessStrategyNumber);
if (OidIsValid(result)) {
break;
}
}
}
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
if (OID_IS_BTREE(aform->amopmethod) && 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;
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
if (aform->amopmethod == HASH_AM_OID && aform->amopstrategy == HTEqualStrategyNumber) {
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;
}
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)) {
if (lhs_opno != NULL)
*lhs_opno = InvalidOid;
continue;
}
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;
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
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;
}
if (rhs_procno == NULL) {
result = true;
break;
}
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)) {
if (lhs_procno != NULL) {
*lhs_procno = InvalidOid;
}
continue;
}
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop op_form = (Form_pg_amop)GETSTRUCT(op_tuple);
StrategyNumber op_strategy;
if (!OID_IS_BTREE(op_form->amopmethod)) {
continue;
}
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop op_form = (Form_pg_amop)GETSTRUCT(op_tuple);
StrategyNumber op_strategy;
if (!OID_IS_BTREE(op_form->amopmethod)) {
continue;
}
op_strategy = (StrategyNumber)op_form->amopstrategy;
Assert(op_strategy >= 1 && op_strategy <= 5);
if (op_strategy != BTEqualStrategyNumber) {
continue;
}
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;
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop op_form = (Form_pg_amop)GETSTRUCT(op_tuple);
if (OID_IS_BTREE(op_form->amopmethod) || 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, bool allowDropped)
{
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;
if (!att_tup->attisdropped) {
result = pstrdup(NameStr(att_tup->attname));
} else if (allowDropped && att_tup->attisdropped) {
result = pstrdup(NameStr(att_tup->attdroppedname));
}
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
* get_kvtype
* Given the relation id and the attribute number,
* return the field type(ATT_KV_UNDEFINED/ATT_KV_TAG/ATT_KV_FIELD/ATT_KV_TIME/ATT_KV_HIDE)
* from the attribute relation.
*
* Note: -1 if no such attribute.
*/
int get_kvtype(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);
int kvtype = att_tup->attkvtype;
ReleaseSysCache(tp);
return kvtype;
} else {
return -1;
}
}
* 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, bool allowDropped)
{
char* attname = NULL;
attname = get_attname(relid, attnum, allowDropped);
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;
}
}
* GetGenerated
*
* Given the relation id and the attribute number,
* return the "generated" field from the attrdef relation.
*
* Errors if not found.
*
* Since not generated is represented by '\0', this can also be used as a
* Boolean test.
*/
char GetGenerated(Oid relid, AttrNumber attnum)
{
char result = '\0';
Relation relation;
TupleDesc tupdesc;
relation = heap_open(relid, NoLock);
tupdesc = RelationGetDescr(relation);
result = GetGeneratedCol(tupdesc, attnum - 1);
heap_close(relation, NoLock);
return result;
}
* 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)) {
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 {
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;
if (opno == ARRAY_EQ_OP) {
typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
if (typentry->hash_proc == F_HASH_ARRAY) {
result = true;
}
} else {
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.
*/
constexpr RegProcedure OPERATOR_EQUALS_PROCEDURE_ID = 101;
RegProcedure get_oprrest(Oid opno)
{
RegProcedure result;
if (opno == OPERATOR_EQUALS_OPNO) {
result = OPERATOR_EQUALS_PROCEDURE_ID;
return result;
}
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;
}
}
void get_operator_types(Oid opno, Oid* leftType, Oid* rightType)
{
HeapTuple tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tuple)) {
Form_pg_operator opForm = (Form_pg_operator)GETSTRUCT(tuple);
*leftType = opForm->oprleft;
*rightType = opForm->oprright;
ReleaseSysCache(tuple);
} else {
*leftType = InvalidOid;
*rightType = 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_owner
*
* Returns the pg_user OID associated with a given function.
*/
Oid get_func_owner(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->proowner;
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;
oidvector* proargs = ProcedureGetArgTypes(tp);
Assert(*nargs == proargs->dim1);
*argtypes = (Oid*)palloc(*nargs * sizeof(Oid));
if (*nargs > 0) {
int rc = memcpy_s(*argtypes, *nargs * sizeof(Oid), proargs->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_langname
* Given procedure id, return the function's language.
* Caller is responsible to free the returned string.
*/
char* get_func_langname(Oid funcid)
{
HeapTuple tp;
Oid langoid;
bool isNull = true;
char* result = NULL;
Relation relation = heap_open(ProcedureRelationId, NoLock);
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function with oid %u does not exist", funcid)));
}
Datum datum = heap_getattr(tp, Anum_pg_proc_prolang, RelationGetDescr(relation), &isNull);
langoid = DatumGetObjectId(datum);
heap_close(relation, NoLock);
ReleaseSysCache(tp);
relation = heap_open(LanguageRelationId, NoLock);
tp = SearchSysCache1(LANGOID, ObjectIdGetDatum(langoid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for language %u", langoid)));
}
datum = heap_getattr(tp, Anum_pg_language_lanname, RelationGetDescr(relation), &isNull);
if (isNull) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for language name %u", langoid)));
}
result = pstrdup(NameStr(*DatumGetName(datum)));
heap_close(relation, NoLock);
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, NoLock);
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, NoLock);
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 = ((Form_pg_proc)GETSTRUCT(tp))->proiswindow;
ReleaseSysCache(tp);
return result;
}
char get_func_prokind(const Oid funcid)
{
HeapTuple tp = NULL;
char result;
bool isnull = false;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", funcid)));
}
Datum prokindDatum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isnull);
if (isnull) {
result = PROKIND_FUNCTION;
} else {
result = DatumGetChar(prokindDatum);
}
ReleaseSysCache(tp);
return result;
}
* get_func_full_name
* returns the full name (schema.pkg.func or schema.func) 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_full_name(Oid funcid)
{
HeapTuple tp;
char* result = NULL;
bool isNull = false;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (HeapTupleIsValid(tp)) {
Form_pg_proc functup = (Form_pg_proc)GETSTRUCT(tp);
char* schemaName = get_namespace_name(functup->pronamespace);
char* proName = NameStr(functup->proname);
Oid pkgOid = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_packageid, &isNull);
char* packageName = NULL;
error_t rc = 0;
size_t len = 0;
if (OidIsValid(pkgOid)) {
packageName = GetPackageName(pkgOid);
len = strlen(schemaName) + strlen(packageName) + strlen(proName) + 3;
result = (char*)palloc0(len);
rc = snprintf_s(result, len, len - 1, "%s.%s.%s", schemaName, packageName, proName);
} else {
len = strlen(schemaName) + strlen(proName) + 2;
result = (char*)palloc0(len);
rc = snprintf_s(result, len, len - 1, "%s.%s", schemaName, proName);
}
securec_check_ss(rc, "", "");
ReleaseSysCache(tp);
return result;
} else {
return NULL;
}
}
* 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);
if (isSupportSynonym && !OidIsValid(relid)) {
objVar = SearchReferencedObject(relname, relnamespace);
if (objVar != NULL) {
objNamespaceOid = get_namespace_oid(objVar->schemaname, true);
relid = get_relname_relid(objVar->relname, objNamespaceOid);
appendStringInfo(&detail, _("%s"), CheckReferencedObject(relid, objVar, relname));
if (refSynOid != NULL && OidIsValid(relid)) {
*refSynOid = GetSynonymOid(relname, relnamespace, true);
}
}
}
if (relOid != NULL) {
*relOid = relid;
}
return detail.data;
}
extern bool StreamTopConsumerAmI();
Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok)
{
Oid nspid = InvalidOid;
Oid oldnspid = InvalidOid;
Oid relid = InvalidOid;
Oid oldrelid = InvalidOid;
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.
*
*/
uint64 sess_inval_count;
uint64 thrd_inval_count = 0;
for (;;) {
sess_inval_count = u_sess->inval_cxt.SIMCounter;
if (EnableLocalSysCache()) {
thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter;
}
nspid = get_namespace_oid(relnamespace, nsp_missing_ok);
if (!OidIsValid(nspid)) {
return InvalidOid;
}
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) {
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 (nspid != oldnspid && OidIsValid(oldnspid)) {
UnlockDatabaseObject(NamespaceRelationId, oldnspid, 0, AccessShareLock);
}
if (relid != oldrelid && OidIsValid(oldrelid)) {
UnlockRelationOid(oldrelid, AccessShareLock);
}
}
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);
}
if (OidIsValid(nspid) && StreamTopConsumerAmI()) {
LockDatabaseObject(NamespaceRelationId, nspid, 0, AccessShareLock);
}
if (EnableLocalSysCache()) {
if (sess_inval_count == u_sess->inval_cxt.SIMCounter &&
thrd_inval_count == t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter) {
break;
}
} else {
if (sess_inval_count == u_sess->inval_cxt.SIMCounter) {
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);
Assert((PART_OBJ_TYPE_TABLE_PARTITION == partition->parttype) ||
(PART_OBJ_TYPE_TABLE_SUB_PARTITION == partition->parttype) ||
(PART_OBJ_TYPE_INDEX_PARTITION == partition->parttype));
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) ||
(reltup->parttype == PARTTYPE_SUBPARTITIONED_RELATION);
ReleaseSysCache(tp);
return result;
} else {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", relid)));
return false;
}
}
* 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);
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_rel_persistence
*
* Returns the relpersistence associated with a given relation.
*/
char get_rel_persistence(Oid relid)
{
HeapTuple tp;
Form_pg_class reltup;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp)) {
reltup = (Form_pg_class) GETSTRUCT(tp);
char result = reltup->relpersistence;
ReleaseSysCache(tp);
return result;
} else {
* for partition table.
* global temp table does not support partitioning.
*/
return '\0';
}
}
* 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.
*/
constexpr int16 DATE_TYPLEN = 4;
constexpr int16 TIMESTAMP_TYPLEN = 8;
int16 get_typlen(Oid typid)
{
if (typid == INT4OID || typid == DATEOID || typid == FLOAT4OID) {
return DATE_TYPLEN;
} else if (typid == BPCHAROID || typid == VARCHAROID) {
return -1;
} else if (typid == TIMESTAMPOID) {
return TIMESTAMP_TYPLEN;
}
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)
{
switch (typid) {
case INT4OID:
case DATEOID: {
*typlen = 4;
*typbyval = true;
} break;
case TIMESTAMPOID:{
*typlen = 8;
*typbyval = true;
} break;
case BPCHAROID:
case VARCHAROID: {
*typlen = -1;
*typbyval = false;
} break;
default: {
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 (typeStruct->typtype == TYPTYPE_TABLEOF) {
return SearchSubTypeByType(typeStruct, NULL);
} else 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) {
expr = (Node*)stringToNode(TextDatumGetCString(datum));
} else {
datum = SysCacheGetAttr(TYPEOID, typeTuple, Anum_pg_type_typdefault, &isNull);
if (!isNull) {
char* strDefaultVal = NULL;
strDefaultVal = TextDatumGetCString(datum);
datum = OidInputFunctionCall(type->typinput, strDefaultVal, getTypeIOParam(typeTuple), -1);
expr = (Node*)makeConst(typid, -1, type->typcollation, type->typlen, datum, false, type->typbyval);
pfree_ext(strDefaultVal);
} else {
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);
}
Oid getBaseTypeAndOtherAttr(Oid typid, int32* typmod, char* typtype)
{
* 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 (typtype != NULL) {
*typtype = typTup->typtype;
}
if (typTup->typtype != TYPTYPE_DOMAIN) {
ReleaseSysCache(tup);
break;
}
Assert(typTup->typinput==F_SUBTYPE_IN || *typmod == -1);
typid = typTup->typbasetype;
*typmod = typTup->typtypmod;
ReleaseSysCache(tup);
}
return typid;
}
* 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)
{
if (typid == DATEOID || typid == TIMESTAMPOID || typid == BPCHAROID || typid == VARCHAROID) {
return typid;
}
return getBaseTypeAndOtherAttr(typid, typmod);
}
#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_with_schema
* Get type schema.name list for given type ID.
*/
List* get_typename_with_schema(Oid typid)
{
HeapTuple tuple;
Form_pg_type typeForm;
List* 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 = list_make2(makeString(get_namespace_name(typeForm->typnamespace)),
makeString(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);
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);
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;
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);
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)));
}
Assert(OidIsValid(namespaceId));
typid = GetSysCacheOid2(TYPENAMENSP, PointerGetDatum(atpname), ObjectIdGetDatum(namespaceId));
pfree_ext(namestr);
list_free_ext(elemlist);
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)
{
#ifdef ENABLE_MULTIPLE_NODES
Oid node_oid = InvalidOid;
CatCList* memlist = NULL;
int i;
memlist = SearchSysCacheList1(PGXCNODENAME, PointerGetDatum(nodename));
for (i = 0; i < memlist->n_members; i++) {
HeapTuple tup = t_thrd.lsc_cxt.FetchTupleFromCatCList(memlist, i);
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;
#else
return InvalidOid;
#endif
}
Oid get_pgxc_datanodeoid(const char* nodename, bool missingOK)
{
Oid dn_oid = InvalidOid;
CatCList* memlist = NULL;
int i;
memlist = SearchSysCacheList1(PGXCNODENAME, PointerGetDatum(nodename));
for (i = 0; i < memlist->n_members; i++) {
HeapTuple tup = t_thrd.lsc_cxt.FetchTupleFromCatCList(memlist, i);
char node_type = ((Form_pgxc_node)GETSTRUCT(tup))->node_type;
if (node_type == PGXC_NODE_DATANODE) {
dn_oid = HeapTupleGetOid(tup);
break;
}
}
ReleaseSysCacheList(memlist);
if (dn_oid == InvalidOid && !missingOK) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("datanode \"%s\" does not exist", nodename)));
}
return dn_oid;
}
* Check if the two given strings indicate the same host.
*/
inline bool IsSameHost(const char* str1, const char* str2){
if (str1 == NULL || str2 == NULL){
return false;
}
return (strcmp(str1, "localhost") == 0 && strcmp(str2, "127.0.0.1") == 0) ||
(strcmp(str1, str2) == 0);
}
* 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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(memlist, i);
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;
if (IsSameHost(host_exist, host)) {
if (port_exist == port) {
node_has_been_def = true;
break;
}
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);
PG_TRY_RETURN();
}
tuple = t_thrd.lsc_cxt.FetchTupleFromCatCList(memlist, 0);
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, NameData* 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->data, NAMEDATALEN, NameStr(nodeForm->node_name), NAMEDATALEN - 1);
securec_check(rc, "\0", "\0");
ReleaseSysCache(tuple);
return nodename->data;
}
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;
}
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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(memlist, 0);
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 {
is_primary_dn = IS_PRIMARY_DATA_NODE(node_type);
}
}
if (is_primary_dn) {
if (strncmp(NameStr(nodeForm->node_host), host, strlen(host)) != 0) {
result = false;
}
} else {
if (IS_DN_DUMMY_STANDYS_MODE()) {
if (strncmp(NameStr(nodeForm->node_host1), host, strlen(host)) != 0) {
result = false;
}
} else {
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)
{
Oid groupoid = InvalidOid;
if (!ng_is_valid_group_name(groupname)) {
return InvalidOid;
}
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)));
}
return groupoid;
}
int get_pgxc_group_bucketcnt(Oid group_oid)
{
HeapTuple tuple;
bool is_null = false;
Datum group_datum;
int bucketcnt = 0;
tuple = SearchSysCache1(PGXCGROUPOID, ObjectIdGetDatum(group_oid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("get_pgxc_group_bucketcnt lookup failed for group %u", group_oid)));
}
group_datum = SysCacheGetAttr(PGXCGROUPOID, tuple, Anum_pgxc_group_buckets, &is_null);
if (is_null) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("get_pgxc_group_bucketcnt invalid bucketcnt for group %u", group_oid)));
}
text_to_bktmap((text*)group_datum, NULL, &bucketcnt);
ReleaseSysCache(tuple);
return bucketcnt;
}
bool is_pgxc_group_bucketcnt_exists(Oid parentOid, int bucketCnt, char **groupName, Oid *groupOid)
{
HeapTuple tuple;
bool ret = false;
Relation rel = heap_open(PgxcGroupRelationId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
bool isnull;
int bucketlen;
Datum val = heap_getattr(tuple, Anum_pgxc_group_parent, RelationGetDescr(rel), &isnull);
if (isnull || parentOid != DatumGetObjectId(val)) {
continue;
}
val = heap_getattr(tuple, Anum_pgxc_group_buckets, RelationGetDescr(rel), &isnull);
if (isnull) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("is_pgxc_group_bucketcnt_exists invalid bucketcnt")));
}
text_to_bktmap((text*)val, NULL, &bucketlen);
if (bucketlen == bucketCnt) {
ret = true;
if (groupName) {
Datum group_name_datum = heap_getattr(tuple, Anum_pgxc_group_name, RelationGetDescr(rel), &isnull);
*groupName = (char*)pstrdup((const char*)DatumGetCString(group_name_datum));
}
if (groupOid) {
*groupOid = HeapTupleGetOid(tuple);
}
break;
}
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
return ret;
}
char* get_pgxc_groupparent(Oid group_oid)
{
HeapTuple tuple;
bool is_null = false;
Datum group_datum;
char *parent = NULL;
Relation rel = heap_open(PgxcGroupRelationId, AccessShareLock);
tuple = SearchSysCache1(PGXCGROUPOID, ObjectIdGetDatum(group_oid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("get_pgxc_group_parentcache lookup failed for group %u", group_oid)));
}
group_datum = SysCacheGetAttr(PGXCGROUPOID, tuple, Anum_pgxc_group_parent, &is_null);
if (!is_null) {
Oid parentOid = DatumGetObjectId(group_datum);
parent = get_pgxc_groupname(parentOid);
}
ReleaseSysCache(tuple);
heap_close(rel, AccessShareLock);
return parent;
}
* 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)));
}
rel = relation_open(PgxcGroupRelationId, NoLock);
Datum group_member_datum = heap_getattr(tuple, Anum_pgxc_group_members, rel->rd_att, &isNull);
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 (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)));
}
rel = relation_open(PgxcGroupRelationId, NoLock);
group_member_datum = heap_getattr(tuple, Anum_pgxc_group_members, rel->rd_att, &isNull);
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);
if (dst_nmembers > nmembers) {
nmembers = dst_nmembers;
if (members != NULL) {
*members = dst_members;
}
} else {
pfree_ext(dst_members);
}
}
}
}
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;
}
static List* GetAllSliceTuples(Relation pgxcSliceRel, Oid tableOid)
{
ScanKeyData skey[2];
SysScanDesc sliceScan = NULL;
HeapTuple htup = NULL;
HeapTuple dtup = NULL;
List* sliceList = NULL;
ScanKeyInit(&skey[0], Anum_pgxc_slice_relid, BTEqualStrategyNumber,
F_OIDEQ, ObjectIdGetDatum(tableOid));
ScanKeyInit(&skey[1], Anum_pgxc_slice_type, BTEqualStrategyNumber,
F_CHAREQ, CharGetDatum(PGXC_SLICE_TYPE_SLICE));
sliceScan = systable_beginscan(pgxcSliceRel, PgxcSliceOrderIndexId, true, NULL, 2, skey);
while (HeapTupleIsValid((htup = systable_getnext(sliceScan)))) {
dtup = heap_copytuple(htup);
sliceList = lappend(sliceList, dtup);
}
systable_endscan(sliceScan);
return sliceList;
}
static List* GetSliceBoundary(Relation rel, Datum boundaries, List* distKeyPosList, bool attIsNull, bool isRangeSlice)
{
List* boundaryValueList = NIL;
List* resultBoundaryList = NIL;
ListCell* boundaryCell = NULL;
Value* boundaryValue = NULL;
Node* boundaryNode = NULL;
bool isFirstDefault = true;
if (attIsNull) {
resultBoundaryList = NIL;
} else {
boundaryValueList = untransformPartitionBoundary(boundaries);
foreach(boundaryCell, boundaryValueList) {
boundaryValue = (Value*)lfirst(boundaryCell);
if (!PointerIsValid(boundaryValue->val.str)) {
if (isRangeSlice) {
boundaryNode = (Node*)makeMaxConst(UNKNOWNOID, -1, InvalidOid);
} else {
if (isFirstDefault) {
boundaryNode = (Node*)makeMaxConst(UNKNOWNOID, -1, InvalidOid);
isFirstDefault = false;
} else {
continue;
}
}
} else {
boundaryNode = (Node*)make_const(NULL, makeString(boundaryValue->val.str), 0);
}
resultBoundaryList = lappend(resultBoundaryList, boundaryNode);
}
}
return resultBoundaryList;
}
static DistState* BuildDistStateHelper(DistributionType distType, List* sliceDefinitions)
{
DistState* distState = makeNode(DistState);
distState->strategy = (distType == DISTTYPE_RANGE) ? 'r' : 'l';
distState->sliceList = sliceDefinitions;
return distState;
}
static DistState* BuildDistState(Relation rel, List* distKeyPosList, DistributionType distType)
{
bool tmpNull, attIsNull, specified;
ListCell* sliceCell = NULL;
List* boundary = NULL;
List* sliceDefinitions = NIL;
ListSliceDefState* listSliceDef = NULL;
ListSliceDefState* lastListSliceDef = NULL;
RangePartitionDefState* rangeSliceDef = NULL;
Relation pgxcSliceRel = heap_open(PgxcSliceRelationId, AccessShareLock);
TupleDesc desc = RelationGetDescr(pgxcSliceRel);
Oid tableOid = RelationGetRelid(rel);
List* sliceList = GetAllSliceTuples(pgxcSliceRel, tableOid);
foreach (sliceCell, sliceList) {
HeapTuple sliceTuple = (HeapTuple)lfirst(sliceCell);
Datum boundDatum = heap_getattr(sliceTuple, Anum_pgxc_slice_boundaries, desc, &attIsNull);
int4 sindex = DatumGetInt32(heap_getattr(sliceTuple, Anum_pgxc_slice_sindex, desc, &tmpNull));
char* sliceName = DatumGetCString(heap_getattr(sliceTuple, Anum_pgxc_slice_relname, desc, &tmpNull));
specified = DatumGetBool(heap_getattr(sliceTuple, Anum_pgxc_slice_specified, desc, &tmpNull));
Oid nodeOid = DatumGetObjectId(heap_getattr(sliceTuple, Anum_pgxc_slice_nodeoid, desc, &tmpNull));
if (distType == DISTTYPE_LIST) {
boundary = GetSliceBoundary(rel, boundDatum, distKeyPosList, attIsNull, false);
if (sindex == 0) {
listSliceDef = makeNode(ListSliceDefState);
listSliceDef->name = pstrdup(sliceName);
if (specified) {
listSliceDef->datanode_name = get_pgxc_nodename(nodeOid, NULL);
}
listSliceDef->boundaries = lappend(listSliceDef->boundaries, boundary);
sliceDefinitions = lappend(sliceDefinitions, listSliceDef);
lastListSliceDef = listSliceDef;
} else {
lastListSliceDef->boundaries = lappend(lastListSliceDef->boundaries, boundary);
}
} else if (distType == DISTTYPE_RANGE) {
rangeSliceDef = makeNode(RangePartitionDefState);
rangeSliceDef->partitionName = pstrdup(sliceName);
if (specified) {
rangeSliceDef->tablespacename = get_pgxc_nodename(nodeOid, NULL);
}
rangeSliceDef->boundary = GetSliceBoundary(rel, boundDatum, distKeyPosList, attIsNull, true);
sliceDefinitions = lappend(sliceDefinitions, rangeSliceDef);
}
}
relation_close(pgxcSliceRel, AccessShareLock);
list_free_ext(sliceList);
DistState* distState = BuildDistStateHelper(distType, sliceDefinitions);
return distState;
}
DistributeBy* getTableDistribution(Oid srcRelid)
{
Relation rel;
HeapTuple tuple;
Form_pgxc_class classForm;
List* distKeyPosList = NULL;
DistributeBy* result = makeNode(DistributeBy);
rel = relation_open(srcRelid, AccessShareLock);
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:
result->disttype = DISTTYPE_HASH;
break;
case LOCATOR_TYPE_REPLICATED:
result->disttype = DISTTYPE_REPLICATION;
break;
case LOCATOR_TYPE_MODULO:
result->disttype = DISTTYPE_MODULO;
break;
case LOCATOR_TYPE_RROBIN:
result->disttype = DISTTYPE_ROUNDROBIN;
break;
case LOCATOR_TYPE_LIST:
result->disttype = DISTTYPE_LIST;
break;
case LOCATOR_TYPE_RANGE:
result->disttype = DISTTYPE_RANGE;
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])));
distKeyPosList = lappend_int(distKeyPosList, classForm->pcattnum.values[i]);
}
if (IsLocatorDistributedBySlice(classForm->pclocatortype)) {
result->distState = BuildDistState(rel, distKeyPosList, result->disttype);
}
ReleaseSysCache(tuple);
list_free_ext(distKeyPosList);
relation_close(rel, AccessShareLock);
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->dim1; 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;
Oid groupoid = InvalidOid;
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 {
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))));
}
Assert(groupname != NULL && !isNull);
groupoid = get_pgxc_groupoid(groupname);
if (HeapTupleIsValid(tuple)) {
ReleaseSysCache(tuple);
}
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;
}
if (maxwidth < 1000) {
return 32 + (maxwidth - 32) / 2;
}
* 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 ||
get_typtype(typid) == TYPTYPE_ABSTRACT_OBJECT);
}
* 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_set
* Returns true if the given type is an set type.
*/
bool type_is_set(Oid typid)
{
return (get_typtype(typid) == TYPTYPE_SET);
}
* 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);
}
* type_is_relation
*
* Convenience function to determine whether a type OID represents
* a ordinaty table type.
*/
bool type_is_relation(Oid typid)
{
if (get_typtype(typid) != TYPTYPE_COMPOSITE) {
return false;
}
Oid relid = get_typ_typrelid(typid);
if (!OidIsValid(relid)) {
return false;
}
return get_rel_relkind(relid) == RELKIND_RELATION;
}
* 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);
char typtype = typtup->typtype;
Oid result;
if (typtup->typlen == -1) {
result = typtup->typelem;
} else {
result = InvalidOid;
}
ReleaseSysCache(tp);
return typtype == TYPTYPE_TABLEOF? get_element_type(result) :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) {
Oid result;
if (typTup->typlen == -1) {
result = typTup->typelem;
} else {
result = InvalidOid;
}
ReleaseSysCache(tup);
return result;
}
typid = typTup->typbasetype;
ReleaseSysCache(tup);
}
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
* get_typcollation
*
* Given the type OID, return the type's typcollation attribute.
*/
Oid get_typcollation(Oid typid)
{
switch (typid) {
case INT4OID:
case DATEOID:
case TIMESTAMPOID:{
return InvalidOid;
} break;
case BPCHAROID:
case VARCHAROID: {
return DEFAULT_COLLATION_OID;
} break;
default: {
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 (!ispartition && get_rel_persistence(relid) == RELPERSISTENCE_GLOBAL_TEMP) {
tp = get_gtt_att_statistic(relid, attnum);
if (!HeapTupleIsValid(tp)) {
return 0;
}
stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
if (stawidth > 0) {
return stawidth;
} else {
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;
val = SysCacheGetAttr(STATRELKINDATTINH, statstuple, Anum_pg_statistic_stadndistinct, &isnull);
if (isnull) {
return 0;
} else {
return DatumGetFloat4(val);
}
}
static void get_attstatsslotnumber(HeapTuple statstuple, float4** numbers, int* nnumbers, int idx)
{
bool isnull = false;
ArrayType* statarray = NULL;
int narrayelem;
Datum val = SysCacheGetAttr(STATRELKINDATTINH, statstuple, Anum_pg_statistic_stanumbers1 + idx, &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);
}
}
* 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;
HeapTuple typeTuple;
Form_pg_type typeForm;
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;
}
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(
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) {
get_attstatsslotnumber(statstuple, numbers, nnumbers, i);
}
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;
}
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(
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;
}
#ifndef ENABLE_MULTIPLE_NODES
* get_slot_index_dependency
* Given a statstuple and a reqkind, return the slot index.
* Return -1, if not found.
*/
int get_slot_index_dependencies(HeapTuple statstuple, int reqkind)
{
Form_pg_statistic_ext stats = (Form_pg_statistic_ext)GETSTRUCT(statstuple);
int slot_index = 0;
for (slot_index = 0; slot_index < STATISTIC_NUM_SLOTS; ++slot_index) {
if ((&stats->stakind1)[slot_index] == reqkind) {
break;
}
}
if (slot_index >= STATISTIC_NUM_SLOTS) {
slot_index = -1;
}
return slot_index;
}
* get_attmultistatsslot_dependencies
* Extract functional dependency relations from PG_STATISTIC_EXT.
* Return TRUE if requested slot type was found, else FALSE.
*
* stakind must be STATISTIC_EXT_DEPENDENCIES. The attributes are saved in stavalues slot and the degrees are saved in
* stanumbers slot. These two lists must have the same length.
* For example:
* stavalues: {.625,.375,.625}
* stanumbers: {"{1,2,3}","{1,3,2}","{2,3,1}"
* We can extract three functional dependency relations as follows
* "1, 2 -> 3 : 0.625"
* "1, 3 -> 2 : 0.375"
* "2, 3 -> 1 : 0.625"
*/
bool get_attmultistatsslot_dependencies(bool dependencies_flag, HeapTuple statstuple, int reqkind,
MVDependencies **dependencies)
{
Datum* values = NULL;
int nvalues;
float4* numbers = NULL;
int nnumbers;
Datum val;
bool isnull = false;
ArrayType* statarray = NULL;
Oid arrayelemtype;
HeapTuple typeTuple;
Form_pg_type typeForm;
int slot_index;
if (!dependencies_flag) {
return false;
}
slot_index = get_slot_index_dependencies(statstuple, reqkind);
if (slot_index == -1) {
return false;
}
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 when extract multi statistics")));
}
statarray = DatumGetArrayTypeP(val);
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 when extract multi statistics", arrayelemtype)));
}
typeForm = (Form_pg_type)GETSTRUCT(typeTuple);
deconstruct_array(statarray, arrayelemtype, typeForm->typlen, typeForm->typbyval, typeForm->typalign, &values, NULL,
&nvalues);
*dependencies = (MVDependencies *)palloc0(offsetof(MVDependencies, deps) + nvalues * sizeof(char *));
(*dependencies)->magic = STATS_DEPS_MAGIC;
(*dependencies)->type = STATS_DEPS_TYPE_BASIC;
(*dependencies)->ndeps = nvalues;
for (int i = 0; i < nvalues; ++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 when extract multi statistics", 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);
(*dependencies)->deps[i] =
(MVDependency *)palloc0(offsetof(MVDependency, attributes) + attr_nvalues * sizeof(AttrNumber));
(*dependencies)->deps[i]->nattributes = (AttrNumber)attr_nvalues;
for (int j = 0; j < attr_nvalues; j++) {
(*dependencies)->deps[i]->attributes[j] = (AttrNumber)(attr_values[j]);
}
* The functional dependency degree is initialized as 0 here in consideration of robustness.
* 0 means no functional dependency relation among these attributes and this statistic info will no be used for
* computing selectivity. It will be updated with its real degree in PART 3.
*/
(*dependencies)->deps[i]->degree = 0;
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);
}
ReleaseSysCache(typeTuple);
if ((Pointer)statarray != DatumGetPointer(val)) {
pfree_ext(statarray);
}
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);
nnumbers = ARR_DIMS(statarray)[0];
if (ARR_NDIM(statarray) != 1 || nnumbers <= 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")));
}
if (nvalues != nnumbers) {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("nvalues isn't equal to nnumbers")));
}
numbers = (float4 *)palloc(nnumbers * sizeof(float4));
errno_t rc = memcpy_s(numbers, nnumbers * sizeof(float4), ARR_DATA_PTR(statarray), nnumbers * sizeof(float4));
securec_check(rc, "\0", "\0");
for (int i = 0; i < nnumbers; i++) {
if (numbers[i] < 0 || numbers[i] > 1) {
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("functional dependency degree must be greater than 0 and less than 1 strictly")));
}
(*dependencies)->deps[i]->degree = numbers[i];
}
pfree_ext(numbers);
if ((Pointer)statarray != DatumGetPointer(val)) {
pfree_ext(statarray);
}
return true;
}
#endif
* 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)
{
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
return NULL;
}
* get_namespace_name_or_temp
* As above, but if it is this backend's temporary namespace, return
* "pg_temp" instead.
*/
char *
get_namespace_name_or_temp(Oid nspid)
{
if (isTempNamespace(nspid))
return "pg_temp";
else
return get_namespace_name(nspid);
}
* 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));
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));
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;
}
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);
*oprleft = operform->oprleft;
*oprright = operform->oprright;
ReleaseSysCache(opertup);
}
* If the return type is polymorphic, continue
* For example: CREATE FUNCTION array_abs(x anyarray) RETURN anyarray ...
* And we have SQL: select array_abs({1,-2,3,-4});
* Obviously, the output type(output_id) is INT, not anyarray
* So we don't check the return type.
*/
static inline bool CompareRetType(Oid inputOid, Oid outputOid)
{
if (!IsPolymorphicType(inputOid)) {
return inputOid == outputOid;
}
return true;
}
Oid get_func_oid(const char* funcname, Oid funcnamespace, Expr* expr, bool noPkg)
{
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++;
}
}
#ifndef ENABLE_MULTIPLE_NODES
if (t_thrd.proc->workingVersionNum < 92470) {
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
} else {
catlist = SearchSysCacheList1(PROCALLARGS, CStringGetDatum(funcname));
}
if (expr) {
HeapTuple proctupl = NULL;
proctupl = SearchSysCache1(PROCOID, ObjectIdGetDatum(((FuncExpr*)expr)->funcid));
if (HeapTupleIsValid(proctupl)) {
Form_pg_proc procform;
char* proname = NULL;
Datum pkgOiddatum;
bool isnull = true;
char* pkgname = NULL;
Oid pkgOid = InvalidOid;
procform = (Form_pg_proc)GETSTRUCT(proctupl);
proname = NameStr(procform->proname);
bool ispackage = SysCacheGetAttr(PROCOID, proctupl, Anum_pg_proc_package, &isnull);
pkgOiddatum = SysCacheGetAttr(PROCOID, proctupl, Anum_pg_proc_packageid, &isnull);
if (!isnull && ispackage) {
pkgOid = DatumGetObjectId(pkgOiddatum);
pkgname = GetPackageName(pkgOid);
}
if (pkgname && OidIsValid(pkgOid)) {
ReleaseSysCacheList(catlist);
ReleaseSysCache(proctupl);
return (((FuncExpr*)expr)->funcid);
}
ReleaseSysCache(proctupl);
}
}
#else
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
#endif
for (i = 0; i < catlist->n_members; i++) {
HeapTuple proctup = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_proc procform = (Form_pg_proc)GETSTRUCT(proctup);
if (OidIsValid(funcnamespace)) {
if (procform->pronamespace != funcnamespace) {
continue;
}
}
if (noPkg) {
bool isnull = true;
bool ispackage = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_package, &isnull);
if (!isnull && ispackage) {
continue;
}
}
if (expr != NULL && IsA(expr, FuncExpr) && procform->pronargs != list_length(((FuncExpr*)expr)->args)
&& procform->pronargs != list_length(((FuncExpr*)expr)->args) + procform->pronargdefaults
#ifdef ENABLE_MULTIPLE_NODES
&& !is_streaming_hash_group_func(funcname, funcnamespace)
#endif
)
continue;
* User will define their own function (UDF).
*
* GaussDB compares the number of input parameters, the type of each parameters, and return type
* to find the corresponding function.
*
* function(arg1, arg2, arg3)
* $$
return ret;
$$
*
* Two Scenario:
* 1) In MPP, same UDF in cn/dn has differnet oid.
* 2) In MPP or SingleNode, UDF in VIEW or pg_rewrite.
*/
if (expr != NULL && IsA(expr, FuncExpr) &&
!CompareRetType(procform->prorettype, ((FuncExpr*)expr)->funcresulttype)) {
* Compare the return oid of the function, if the returned Oid doesnot match,
* it means it's not the function we want.
*/
continue;
}
* Support the same function name, same number of arguments and different argument type
*/
if (expr != NULL && IsA(expr, FuncExpr)
#ifdef ENABLE_MULTIPLE_NODES
&& !is_streaming_hash_group_func(funcname, funcnamespace)
#endif
) {
int j = 0;
bool matched = true;
oidvector* proargs = ProcedureGetArgTypes(proctup);
for (j = 0; j < nargs; j++) {
if (!CompareRetType(proargs->values[j], argtypes[j])) {
matched = false;
break;
}
}
if (!matched)
continue;
}
Oid func_oid = HeapTupleGetOid(proctup);
ReleaseSysCacheList(catlist);
return func_oid;
}
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;
#ifndef ENABLE_MULTIPLE_NODES
if (t_thrd.proc->workingVersionNum < 92470) {
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
} else {
catlist = SearchSysCacheList1(PROCALLARGS, CStringGetDatum(funcname));
}
#else
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
#endif
for (i = 0; i < catlist->n_members; i++) {
HeapTuple proctup = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_proc procform = (Form_pg_proc)GETSTRUCT(proctup);
if (OidIsValid(funcnamespace)) {
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
*/
oidvector* proargs = ProcedureGetArgTypes(proctup);
for (j = 0; j < funcnargs; j++) {
if (funcargstype[j] != proargs->values[j]) {
break;
}
}
if (j < funcnargs) {
continue;
}
Oid func_oid = HeapTupleGetOid(proctup);
ReleaseSysCacheList(catlist);
return func_oid;
}
ReleaseSysCacheList(catlist);
return InvalidOid;
}
Oid partid_get_parentid(Oid partid)
{
Form_pg_partition partitionForm;
HeapTuple partitionTup;
Oid relid;
partitionTup = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid));
if (!HeapTupleIsValid(partitionTup)) {
return InvalidOid;
}
partitionForm = (Form_pg_partition)GETSTRUCT(partitionTup);
relid = partitionForm->parentid;
ReleaseSysCache(partitionTup);
return relid;
}
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 && func_form->proisagg == false) {
ReleaseSysCache(func_tuple);
return true;
}
ReleaseSysCache(func_tuple);
return false;
}
char get_typecategory(Oid typid)
{
HeapTuple tuple;
Form_pg_type typeForm;
char result;
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 = typeForm->typcategory;
ReleaseSysCache(tuple);
return result;
}
#ifdef USE_SPQ
* get_check_constraint_relid
* Given check constraint id, return the check constraint's relation oid
*/
Oid get_check_constraint_relid(Oid oidCheckconstraint)
{
HeapTuple tp;
tp = SearchSysCache(CONSTROID, ObjectIdGetDatum(oidCheckconstraint), 0, 0, 0);
if (HeapTupleIsValid(tp)) {
Form_pg_constraint contup = (Form_pg_constraint)GETSTRUCT(tp);
Oid result;
result = contup->conrelid;
ReleaseSysCache(tp);
return result;
} else
return InvalidOid;
}
* get_check_constraint_oids
* Extract all check constraint oid for a given relation.
*/
List *get_check_constraint_oids(Oid oidRel)
{
List *plConstraints = NIL;
HeapTuple htup;
Relation conrel;
ScanKeyData scankey;
SysScanDesc sscan;
* lookup constraints for relation from the catalog table
*
* SELECT * FROM pg_constraint WHERE conrelid = :1
*/
conrel = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&scankey, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(oidRel));
sscan = systable_beginscan(conrel, InvalidOid, false, NULL, 1, &scankey);
while (HeapTupleIsValid(htup = systable_getnext(sscan))) {
Form_pg_constraint contuple = (Form_pg_constraint)GETSTRUCT(htup);
if (CONSTRAINT_CHECK != contuple->contype || !contuple->convalidated) {
continue;
}
plConstraints = lappend_oid(plConstraints, HeapTupleGetOid(htup));
}
systable_endscan(sscan);
heap_close(conrel, AccessShareLock);
return plConstraints;
}
* get_check_constraint_expr_tree
* returns the expression node tree representing the check constraint
* with the given oidConstraint.
*
* Note: returns a palloc'd expression node tree, or NULL if no such constraint.
*/
Node *get_check_constraint_expr_tree(Oid oidCheckconstraint)
{
HeapTuple tp;
Node *result = NULL;
tp = SearchSysCache(CONSTROID, ObjectIdGetDatum(oidCheckconstraint), 0, 0, 0);
if (HeapTupleIsValid(tp)) {
Datum conbin;
bool isnull;
conbin = SysCacheGetAttr(CONSTROID, tp, Anum_pg_constraint_conbin, &isnull);
if (!isnull)
result = (Node *)stringToNode(TextDatumGetCString(conbin));
ReleaseSysCache(tp);
}
return result;
}
* operator_exists
* Is there an operator with the given oid
*/
bool operator_exists(Oid oid)
{
return SearchSysCacheExists(OPEROID, oid, 0, 0, 0);
}
* relation_exists
* Is there a relation with the given oid
*/
bool relation_exists(Oid oid)
{
return SearchSysCacheExists(RELOID, oid, 0, 0, 0);
}
* get_agg_transtype
* Given aggregate id, return the aggregate transition function's result type.
*/
Oid get_agg_transtype(Oid aggid)
{
HeapTuple tp;
Oid result;
tp = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for aggregate %u", aggid);
result = ((Form_pg_aggregate)GETSTRUCT(tp))->aggtranstype;
ReleaseSysCache(tp);
return result;
}
* is_agg_partial_capable
* Given aggregate id, check if it can be used in 2-phase aggregation.
*
* It must have a combine function, and if the transition type is 'internal',
* also serial/deserial functions.
*/
bool is_agg_partial_capable(Oid aggid)
{
HeapTuple aggTuple;
Form_pg_aggregate aggform;
bool result = true;
aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggid));
if (!HeapTupleIsValid(aggTuple))
elog(ERROR, "cache lookup failed for aggregate %u", aggid);
aggform = (Form_pg_aggregate)GETSTRUCT(aggTuple);
ReleaseSysCache(aggTuple);
return result;
}
* is_ordered_agg
* Given aggregate id, check if it is an ordered aggregate
*/
bool is_agg_ordered(Oid aggid)
{
HeapTuple aggTuple;
char aggkind;
bool isnull = false;
aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggid));
if (!HeapTupleIsValid(aggTuple))
elog(ERROR, "cache lookup failed for aggregate %u", aggid);
aggkind = DatumGetChar(SysCacheGetAttr(AGGFNOID, aggTuple, Anum_pg_aggregate_aggkind, &isnull));
Assert(!isnull);
ReleaseSysCache(aggTuple);
return AGGKIND_IS_ORDERED_SET(aggkind);
}
* get_cast_func
* finds the cast function between the given source and destination type,
* and records its oid and properties in the output parameters.
* Returns true if a cast exists, false otherwise.
*/
bool get_cast_func(Oid oidSrc, Oid oidDest, bool *is_binary_coercible, Oid *oidCastFunc, CoercionPathType *pathtype)
{
if (IsBinaryCoercible(oidSrc, oidDest)) {
*is_binary_coercible = true;
*oidCastFunc = 0;
return true;
}
*is_binary_coercible = false;
*pathtype = find_coercion_pathway(oidDest, oidSrc, COERCION_IMPLICIT, oidCastFunc);
if (*pathtype == COERCION_PATH_RELABELTYPE)
*is_binary_coercible = true;
if (*pathtype != COERCION_PATH_NONE)
return true;
return false;
}
* type_exists
* Is there a type with the given oid
*/
bool type_exists(Oid oid)
{
return SearchSysCacheExists(TYPEOID, oid, 0, 0, 0);
}
* get_comparison_type
* Retrieve comparison type
*/
SPQCmpType get_comparison_type(Oid oidOp)
{
OpBtreeInterpretation *opBti;
List *opBtis;
opBtis = get_op_btree_interpretation(oidOp);
if (opBtis == NIL) {
return CmptOther;
}
* XXX: Arbitrarily use the first found operator family. Usually
* there is only one, but e.g. if someone has created a reverse ordering
* family that sorts in descending order, it is ambiguous whether a
* < operator stands for the less than operator of the ascending opfamily,
* or the greater than operator for the descending opfamily.
*/
opBti = (OpBtreeInterpretation*)linitial(opBtis);
switch (opBti->strategy) {
case BTLessStrategyNumber:
return CmptLT;
case BTLessEqualStrategyNumber:
return CmptLEq;
case BTEqualStrategyNumber:
return CmptEq;
case BTGreaterEqualStrategyNumber:
return CmptGEq;
case BTGreaterStrategyNumber:
return CmptGT;
case ROWCOMPARE_NE:
return CmptNEq;
default:
elog(ERROR, "unknown B-tree strategy: %d", opBti->strategy);
return CmptOther;
}
}
* index_exists
* Is there an index with the given oid
*/
bool index_exists(Oid oid)
{
return SearchSysCacheExists(INDEXRELID, oid, 0, 0, 0);
}
bool aggregate_exists(Oid oid)
{
return SearchSysCacheExists(AGGFNOID, oid, 0, 0, 0);
}
* get_aggregate
* Get oid of aggregate with given name and argument type
*/
Oid get_aggregate(const char *aggname, Oid oidType)
{
CatCList *catlist;
int i;
Oid oidResult;
catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum((char *)aggname));
oidResult = InvalidOid;
for (i = 0; i < catlist->n_members; i++) {
HeapTuple htup = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Oid oidProc = HeapTupleGetOid(htup);
Form_pg_proc proctuple = (Form_pg_proc)GETSTRUCT(htup);
if (1 != proctuple->pronargs || oidType != proctuple->proargtypes.values[0]) {
continue;
}
if (SearchSysCacheExists(AGGFNOID, ObjectIdGetDatum(oidProc), 0, 0, 0)) {
oidResult = oidProc;
break;
}
}
ReleaseSysCacheList(catlist);
return oidResult;
}
* function_exists
* Is there a function with the given oid
*/
bool function_exists(Oid oid)
{
return SearchSysCacheExists(PROCOID, oid, 0, 0, 0);
}
* check_constraint_exists
* Is there a check constraint with the given oid
*/
bool check_constraint_exists(Oid oidCheckconstraint)
{
return SearchSysCacheExists1(CONSTROID, ObjectIdGetDatum(oidCheckconstraint));
}
* get_check_constraint_name
* returns the name of the check constraint with the given oidConstraint.
*
* Note: returns a palloc'd copy of the string, or NULL if no such constraint.
*/
char *get_check_constraint_name(Oid oidCheckconstraint)
{
return get_constraint_name(oidCheckconstraint);
}
* get_comparison_operator
* Retrieve comparison operator between given types
*/
Oid get_comparison_operator(Oid oidLeft, Oid oidRight, SPQCmpType cmpt)
{
int16 opstrat;
HeapTuple ht;
Oid result = InvalidOid;
Relation pg_amop;
ScanKeyData scankey[4];
SysScanDesc sscan;
switch (cmpt) {
case CmptLT:
opstrat = BTLessStrategyNumber;
break;
case CmptLEq:
opstrat = BTLessEqualStrategyNumber;
break;
case CmptEq:
opstrat = BTEqualStrategyNumber;
break;
case CmptGEq:
opstrat = BTGreaterEqualStrategyNumber;
break;
case CmptGT:
opstrat = BTGreaterStrategyNumber;
break;
default:
return InvalidOid;
}
pg_amop = heap_open(AccessMethodOperatorRelationId, AccessShareLock);
* SELECT * FROM pg_amop
* WHERE amoplefttype = :1 and amoprighttype = :2 and amopmethod = :3 and amopstrategy = :4
*/
ScanKeyInit(&scankey[0], Anum_pg_amop_amoplefttype, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(oidLeft));
ScanKeyInit(&scankey[1], Anum_pg_amop_amoprighttype, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(oidRight));
ScanKeyInit(&scankey[2], Anum_pg_amop_amopmethod, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(BTREE_AM_OID));
ScanKeyInit(&scankey[3], Anum_pg_amop_amopstrategy, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(opstrat));
sscan = systable_beginscan(pg_amop, InvalidOid, false, NULL, 4, scankey);
while (HeapTupleIsValid(ht = systable_getnext(sscan))) {
Form_pg_amop amoptup = (Form_pg_amop)GETSTRUCT(ht);
result = amoptup->amopopr;
break;
}
systable_endscan(sscan);
heap_close(pg_amop, AccessShareLock);
return result;
}
* pfree_ptr_array
* Free an array of pointers, after freeing each individual element
*/
void pfree_ptr_array(char **ptrarray, int nelements)
{
int i;
if (NULL == ptrarray)
return;
for (i = 0; i < nelements; i++) {
if (NULL != ptrarray[i]) {
pfree(ptrarray[i]);
}
}
pfree(ptrarray);
}
* get_func_output_arg_types
* Given procedure id, return the function's output argument types
*/
List *get_func_output_arg_types(Oid funcid)
{
HeapTuple tp;
int numargs;
Oid *argtypes = NULL;
char **argnames = NULL;
char *argmodes = NULL;
List *l_argtypes = NIL;
int i;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", funcid);
numargs = get_func_arg_info(tp, &argtypes, &argnames, &argmodes);
if (NULL == argmodes) {
pfree_ptr_array(argnames, numargs);
if (NULL != argtypes) {
pfree(argtypes);
}
ReleaseSysCache(tp);
return NULL;
}
for (i = 0; i < numargs; i++) {
Oid argtype = argtypes[i];
char argmode = argmodes[i];
if (PROARGMODE_INOUT == argmode || PROARGMODE_OUT == argmode || PROARGMODE_TABLE == argmode) {
l_argtypes = lappend_oid(l_argtypes, argtype);
}
}
pfree_ptr_array(argnames, numargs);
pfree(argtypes);
pfree(argmodes);
ReleaseSysCache(tp);
return l_argtypes;
}
* func_data_access
* Given procedure id, return the function's data access flag.
*/
char func_data_access(Oid funcid)
{
return PRODATAACCESS_NONE;
}
* func_exec_location
* Given procedure id, return the function's proexeclocation field
*/
char func_exec_location(Oid funcid)
{
return PRODATAACCESS_ANY;
}
* get_func_arg_types
* Given procedure id, return all the function's argument types
*/
List *get_func_arg_types(Oid funcid)
{
HeapTuple tp;
Form_pg_proc procstruct;
oidvector *args;
List *result = NIL;
tp = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", funcid);
procstruct = (Form_pg_proc)GETSTRUCT(tp);
args = &procstruct->proargtypes;
for (int i = 0; i < args->dim1; i++) {
result = lappend_oid(result, args->values[i]);
}
ReleaseSysCache(tp);
return result;
}
* get_type_name
* returns the name of the type with the given oid
*
* Note: returns a palloc'd copy of the string, or NULL if no such type.
*/
char *get_type_name(Oid oid)
{
HeapTuple tp;
tp = SearchSysCache(TYPEOID, ObjectIdGetDatum(oid), 0, 0, 0);
if (HeapTupleIsValid(tp)) {
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
char *result;
result = pstrdup(NameStr(typtup->typname));
ReleaseSysCache(tp);
return result;
} else
return NULL;
}
* get_att_stats
* Get attribute statistics. Return a copy of the HeapTuple object, or NULL
* if no stats found for attribute
*
*/
HeapTuple get_att_stats(Oid relid, AttrNumber attrnum)
{
HeapTuple result;
char stakind = STARELKIND_CLASS;
* This is used by ORCA, and ORCA doesn't know that there are two different kinds of stats,
* the inherited stats and the non-inherited. Use the inherited stats, i.e. stats that
* cover all the child tables, too, if available.
*/
result = SearchSysCacheCopy4(STATRELATTINH,
ObjectIdGetDatum(relid),
CharGetDatum(stakind),
Int16GetDatum(attrnum),
BoolGetDatum(true));
if (!result)
result = SearchSysCacheCopy4(STATRELATTINH,
ObjectIdGetDatum(relid),
CharGetDatum(stakind),
Int16GetDatum(attrnum),
BoolGetDatum(false));
return result;
}
* has_subclass_slow
*
* Performs the exhaustive check whether a relation has a subclass. This is
* different from has_subclass(), in that the latter can return true if a relation.
* *might* have a subclass. See comments in has_subclass() for more details.
*/
bool has_subclass_slow(Oid relationId)
{
ScanKeyData scankey;
Relation rel;
SysScanDesc sscan;
bool result;
if (!has_subclass(relationId)) {
return false;
}
rel = heap_open(InheritsRelationId, AccessShareLock);
ScanKeyInit(&scankey, Anum_pg_inherits_inhparent, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));
sscan = systable_beginscan(rel, InvalidOid, false, NULL, 1, &scankey);
result = (systable_getnext(sscan) != NULL);
systable_endscan(sscan);
heap_close(rel, AccessShareLock);
return result;
}
* get_index_opfamilies
* Get the oid of operator families for the index keys
*/
List *get_index_opfamilies(Oid oidIndex)
{
HeapTuple htup;
List *opfam_oids;
bool isnull = false;
int indnkeyatts;
Datum indclassDatum;
oidvector *indclass;
htup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oidIndex));
if (!HeapTupleIsValid(htup))
elog(ERROR, "Index %u not found", oidIndex);
* use SysCacheGetAttr() to retrieve number of index attributes, and the oid
* vector of indclass
*/
indnkeyatts = DatumGetInt16(SysCacheGetAttr(INDEXRELID, htup, Anum_pg_index_indnkeyatts, &isnull));
Assert(!isnull);
indclassDatum = SysCacheGetAttr(INDEXRELID, htup, Anum_pg_index_indclass, &isnull);
if (isnull)
return NIL;
indclass = (oidvector *)DatumGetPointer(indclassDatum);
opfam_oids = NIL;
for (int i = 0; i < indnkeyatts; i++) {
Oid oidOpClass = indclass->values[i];
Oid opfam = get_opclass_family(oidOpClass);
opfam_oids = lappend_oid(opfam_oids, opfam);
}
ReleaseSysCache(htup);
return opfam_oids;
}
* Relation->relkind == RELKIND_PARTITIONED_TABLE instead. They should
* have the relcache entry at hand anyway.
*/
bool relation_is_partitioned(Oid relid)
{
HeapTuple tuple;
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tuple)) {
ReleaseSysCache(tuple);
return true;
} else
return false;
}
bool index_is_partitioned(Oid relid)
{
HeapTuple tuple;
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", relid);
Form_pg_class pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
ReleaseSysCache(tuple);
return pg_class_tuple->relkind == RELKIND_GLOBAL_INDEX;
}
* get_operator_opfamilies
* Get the oid of operator families the given operator belongs to
*
* MPP calls this.
*/
List *get_operator_opfamilies(Oid opno)
{
List *opfam_oids;
CatCList *catlist;
int i;
opfam_oids = NIL;
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++) {
HeapTuple htup = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop amop_tuple = (Form_pg_amop)GETSTRUCT(htup);
opfam_oids = lappend_oid(opfam_oids, amop_tuple->amopfamily);
}
ReleaseSysCacheList(catlist);
return opfam_oids;
}
Oid get_compatible_hash_opfamily(Oid opno)
{
Oid result = InvalidOid;
CatCList *catlist;
int i;
* 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 = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i);
Form_pg_amop aform = (Form_pg_amop)GETSTRUCT(tuple);
if (aform->amopmethod == HASH_AM_OID && aform->amopstrategy == HTEqualStrategyNumber) {
result = aform->amopfamily;
break;
}
}
ReleaseSysCacheList(catlist);
return result;
}
Oid get_compatible_legacy_hash_opfamily(Oid opno)
{
return InvalidOid;
}
* child_triggers
* Return true if the table is partitioned and any of the child partitions
* have a trigger of the given type.
*/
bool child_triggers(Oid relationId, int32 triggerType)
{
return false;
}
* get_trigger_type
* Given trigger id, return the trigger's type
*/
int32 get_trigger_type(Oid triggerid)
{
Relation rel;
HeapTuple tp;
int32 result = -1;
ScanKeyData scankey;
SysScanDesc sscan;
ScanKeyInit(&scankey, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(triggerid));
rel = heap_open(TriggerRelationId, AccessShareLock);
sscan = systable_beginscan(rel, TriggerOidIndexId, true, NULL, 1, &scankey);
tp = systable_getnext(sscan);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for trigger %u", triggerid);
result = ((Form_pg_trigger)GETSTRUCT(tp))->tgtype;
systable_endscan(sscan);
heap_close(rel, AccessShareLock);
return result;
}
* trigger_enabled
* Given trigger id, return the trigger's enabled flag
*/
bool trigger_enabled(Oid triggerid)
{
Relation rel;
HeapTuple tp;
bool result = false;
ScanKeyData scankey;
SysScanDesc sscan;
ScanKeyInit(&scankey, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(triggerid));
rel = heap_open(TriggerRelationId, AccessShareLock);
sscan = systable_beginscan(rel, TriggerOidIndexId, true, NULL, 1, &scankey);
tp = systable_getnext(sscan);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for trigger %u", triggerid);
char tgenabled = ((Form_pg_trigger)GETSTRUCT(tp))->tgenabled;
switch (tgenabled) {
case TRIGGER_FIRES_ON_ORIGIN:
* FIXME: we should probably return false when
* SessionReplicationRole isn't SESSION_REPLICATION_ROLE_ORIGIN,
* but does that means we'll also have to flush ORCA's metadata
* cache on every assignment of session_replication_role?
*/
case TRIGGER_FIRES_ALWAYS:
result = true;
break;
case TRIGGER_FIRES_ON_REPLICA:
case TRIGGER_DISABLED:
result = false;
break;
default:
elog(ERROR, "Unknown trigger type: %c", tgenabled);
}
systable_endscan(sscan);
heap_close(rel, AccessShareLock);
return result;
}
bool has_update_triggers(Oid relid)
{
Relation relation;
bool result = false;
relation = heap_open(relid, NoLock);
if (relation->rd_rel->relhastriggers) {
bool found = false;
if (relation->trigdesc == NULL)
RelationBuildTriggers(relation);
if (relation->trigdesc) {
for (int i = 0; i < relation->trigdesc->numtriggers && !found; i++) {
Trigger trigger = relation->trigdesc->triggers[i];
found = trigger_enabled(trigger.tgoid) &&
(get_trigger_type(trigger.tgoid) & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE;
if (found)
break;
}
}
if (found || child_triggers(relation->rd_id, TRIGGER_TYPE_UPDATE))
result = true;
}
heap_close(relation, NoLock);
return result;
}
bool spq_get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple, int reqkind, Oid reqop, int flags)
{
Form_pg_statistic stats = (Form_pg_statistic)GETSTRUCT(statstuple);
int i;
Datum val;
bool isnull;
ArrayType *statarray;
Oid arrayelemtype;
int narrayelem;
HeapTuple typeTuple;
Form_pg_type typeForm;
memset(sslot, 0, sizeof(AttStatsSlot));
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;
sslot->staop = (&stats->staop1)[i];
* XXX Hopefully-temporary hack: if stacoll isn't set, inject the default
* collation. This won't matter for non-collation-aware datatypes. For
* those that are, this covers cases where stacoll has not been set. In
* the short term we need this because some code paths involving type NAME
* do not pass any collation to prefix_selectivity and related functions.
* Even when that's been fixed, it's likely that some add-on typanalyze
* functions won't get the word right away about filling stacoll during
* ANALYZE, so we'll probably need this for awhile.
*/
if (flags & ATTSTATSSLOT_VALUES) {
val = SysCacheGetAttr(STATRELATTINH, statstuple, Anum_pg_statistic_stavalues1 + i, &isnull);
if (isnull)
elog(ERROR, "stavalues is null");
* Detoast the array if needed, and in any case make a copy that's
* under control of this AttStatsSlot.
*/
statarray = DatumGetArrayTypePCopy(val);
* Extract the actual array element type, and pass it back in case the
* caller needs it.
*/
sslot->valuetype = arrayelemtype = ARR_ELEMTYPE(statarray);
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", arrayelemtype);
typeForm = (Form_pg_type)GETSTRUCT(typeTuple);
deconstruct_array(statarray, arrayelemtype, typeForm->typlen, typeForm->typbyval, typeForm->typalign,
&sslot->values, NULL, &sslot->nvalues);
* If the element type is pass-by-reference, we now have a bunch of
* Datums that are pointers into the statarray, so we need to keep
* that until free_attstatsslot. Otherwise, all the useful info is in
* sslot->values[], so we can free the array object immediately.
*/
if (!typeForm->typbyval)
sslot->values_arr = statarray;
else
pfree(statarray);
ReleaseSysCache(typeTuple);
}
if (flags & ATTSTATSSLOT_NUMBERS) {
val = SysCacheGetAttr(STATRELATTINH, statstuple, Anum_pg_statistic_stanumbers1 + i, &isnull);
if (isnull)
elog(ERROR, "stanumbers is null");
* Detoast the array if needed, and in any case make a copy that's
* under control of this AttStatsSlot.
*/
statarray = DatumGetArrayTypePCopy(val);
narrayelem = ARR_DIMS(statarray)[0];
sslot->numbers = (float4 *)ARR_DATA_PTR(statarray);
sslot->nnumbers = narrayelem;
sslot->numbers_arr = statarray;
}
return true;
}
void spq_free_attstatsslot(AttStatsSlot *sslot)
{
if (sslot->values)
pfree(sslot->values);
if (sslot->values_arr)
pfree(sslot->values_arr);
if (sslot->numbers_arr)
pfree(sslot->numbers_arr);
}
bool spq_relation_not_partitioned(Oid relid)
{
HeapTuple tuple;
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tuple)) {
ReleaseSysCache(tuple);
return false;
} else
return true;
}
#endif
Oid get_array_internal_depend_type_oid(Oid arrTypOid)
{
Oid elemOid = get_element_type(arrTypOid);
if (elemOid == InvalidOid) {
return InvalidOid;
}
Oid arrOid = get_array_type(elemOid);
if (arrOid == arrTypOid) {
return elemOid;
}
return InvalidOid;
}