From 21836aa6084d30e241970b2d09a66d021f30d912 Mon Sep 17 00:00:00 2001 From: David Geier Date: Thu, 3 Sep 2026 15:25:09 +0200 Subject: [PATCH v2 8/8] Remove rd_index --- contrib/amcheck/verify_common.c | 2 +- contrib/amcheck/verify_nbtree.c | 2 +- contrib/pgstattuple/pgstatindex.c | 6 +- contrib/pgstattuple/pgstattuple.c | 2 +- src/backend/access/brin/brin.c | 4 +- src/backend/access/common/toast_internals.c | 6 +- src/backend/access/gin/ginfast.c | 2 +- src/backend/access/heap/heapam.c | 4 +- src/backend/access/index/genam.c | 6 +- src/backend/access/index/indexam.c | 2 +- src/backend/access/nbtree/nbtutils.c | 2 +- src/backend/access/spgist/spgutils.c | 12 +- src/backend/catalog/catalog.c | 4 +- src/backend/catalog/index.c | 12 +- src/backend/catalog/indexing.c | 4 +- src/backend/commands/indexcmds.c | 14 +- src/backend/commands/matview.c | 4 +- src/backend/commands/propgraphcmds.c | 1959 +++++++++++++++++++ src/backend/commands/repack.c | 20 +- src/backend/commands/tablecmds.c | 44 +- src/backend/commands/vacuum.c | 2 +- src/backend/executor/execIndexing.c | 16 +- src/backend/executor/execPartition.c | 4 +- src/backend/executor/execReplication.c | 2 +- src/backend/optimizer/util/plancat.c | 10 +- src/backend/parser/parse_utilcmd.c | 2 +- src/backend/replication/logical/conflict.c | 2 +- src/backend/replication/logical/relation.c | 8 +- src/backend/statistics/stat_utils.c | 4 +- src/backend/storage/lmgr/predicate.c | 8 +- src/backend/utils/adt/ri_triggers.c | 6 +- src/backend/utils/cache/catcache.c | 4 +- src/backend/utils/cache/relcache.c | 47 +- src/include/utils/rel.h | 22 +- src/include/utils/rel_internal.h | 1 - 35 files changed, 2112 insertions(+), 137 deletions(-) create mode 100644 src/backend/commands/propgraphcmds.c diff --git a/contrib/amcheck/verify_common.c b/contrib/amcheck/verify_common.c index 46e53b3f99a..5e7228287ba 100644 --- a/contrib/amcheck/verify_common.c +++ b/contrib/amcheck/verify_common.c @@ -181,7 +181,7 @@ index_checkable(Relation rel, Oid am_id) errdetail("Index \"%s\" is associated with temporary relation.", RelationGetRelationName(rel)))); - if (!rel->rd_index->indisvalid) + if (!RelationGetIndex(rel)->indisvalid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot check index \"%s\"", diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index c5e1f901098..d7b7c6d9807 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -456,7 +456,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace, * horizon. Throwing an error here should be very rare. It doesn't * seem worth using a secondary snapshot to avoid this. */ - if (IsolationUsesXactSnapshot() && rel->rd_index->indcheckxmin && + if (IsolationUsesXactSnapshot() && RelationGetIndex(rel)->indcheckxmin && !TransactionIdPrecedes(HeapTupleHeaderGetXmin(rel->rd_indextuple->t_data), state->snapshot->xmin)) ereport(ERROR, diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index 8951ad0aac4..ae0fba426f3 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -244,7 +244,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo) * but the results could be confusing. For example, the index's size * could be too low for a valid index of the table. */ - if (!rel->rd_index->indisvalid) + if (!RelationGetIndex(rel)->indisvalid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("index \"%s\" is not valid", @@ -558,7 +558,7 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo) errmsg("cannot access temporary indexes of other sessions"))); /* see pgstatindex_impl */ - if (!rel->rd_index->indisvalid) + if (!RelationGetIndex(rel)->indisvalid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("index \"%s\" is not valid", @@ -648,7 +648,7 @@ pgstathashindex(PG_FUNCTION_ARGS) errmsg("cannot access temporary indexes of other sessions"))); /* see pgstatindex_impl */ - if (!rel->rd_index->indisvalid) + if (!RelationGetIndex(rel)->indisvalid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("index \"%s\" is not valid", diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index 6a7f8cb4a7c..89116b52250 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -262,7 +262,7 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo) else if (rel->rd_rel->relkind == RELKIND_INDEX) { /* see pgstatindex_impl */ - if (!rel->rd_index->indisvalid) + if (!RelationGetIndex(rel)->indisvalid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("index \"%s\" is not valid", diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 5059da8dce7..8eb25c1e175 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1469,7 +1469,7 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* see gin_clean_pending_list() */ - if (indexRel->rd_index->indisvalid) + if (RelationGetIndex(indexRel)->indisvalid) brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL); else ereport(DEBUG1, @@ -1558,7 +1558,7 @@ brin_desummarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* see gin_clean_pending_list() */ - if (indexRel->rd_index->indisvalid) + if (RelationGetIndex(indexRel)->indisvalid) { /* the revmap does the hard work */ do diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c index b0bfc1bfc9c..dca90ab7145 100644 --- a/src/backend/access/common/toast_internals.c +++ b/src/backend/access/common/toast_internals.c @@ -349,11 +349,11 @@ toast_save_datum(Relation rel, Datum value, for (int i = 0; i < num_indexes; i++) { /* Only index relations marked as ready can be updated */ - if (toastidxs[i]->rd_index->indisready) + if (RelationGetIndex(toastidxs[i])->indisready) index_insert(toastidxs[i], t_values, t_isnull, &(toasttup->t_self), toastrel, - toastidxs[i]->rd_index->indisunique ? + RelationGetIndex(toastidxs[i])->indisunique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO, false, NULL); } @@ -645,7 +645,7 @@ toast_open_indexes(Relation toastrel, { Relation toastidx = (*toastidxs)[i]; - if (toastidx->rd_index->indisvalid) + if (RelationGetIndex(toastidx)->indisvalid) { res = i; found = true; diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c index 46fc60115a8..df5e3545a19 100644 --- a/src/backend/access/gin/ginfast.c +++ b/src/backend/access/gin/ginfast.c @@ -1075,7 +1075,7 @@ gin_clean_pending_list(PG_FUNCTION_ARGS) * is merely awaiting missed aminsert calls, we're capable of processing * it. Decline to do so, out of an abundance of caution. */ - if (indexRel->rd_index->indisvalid) + if (RelationGetIndex(indexRel)->indisvalid) { GinState ginstate; diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index b1279ba00c7..0212fb71083 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -4423,7 +4423,7 @@ check_lock_if_inplace_updateable_rel(Relation relation, { Relation irel = index_open(relid, AccessShareLock); - SET_LOCKTAG_RELATION(tag, dbid, irel->rd_index->indrelid); + SET_LOCKTAG_RELATION(tag, dbid, RelationGetIndex(irel)->indrelid); index_close(irel, AccessShareLock); } else @@ -4477,7 +4477,7 @@ check_inplace_rel_lock(HeapTuple oldtup) { Relation irel = index_open(relid, AccessShareLock); - SET_LOCKTAG_RELATION(tag, dbid, irel->rd_index->indrelid); + SET_LOCKTAG_RELATION(tag, dbid, RelationGetIndex(irel)->indrelid); index_close(irel, AccessShareLock); } else diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 98ac5bc79c9..6879a7678bf 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -211,7 +211,7 @@ BuildIndexValueDescription(Relation indexRelation, * Next we need to check table-level SELECT access and then, if there is * no access there, check column-level permissions. */ - idxrec = indexRelation->rd_index; + idxrec = RelationGetIndex(indexRelation); indrelid = idxrec->indrelid; Assert(indexrelid == idxrec->indexrelid); @@ -456,7 +456,7 @@ systable_beginscan(Relation heapRelation, for (j = 0; j < IndexRelationGetNumberOfAttributes(irel); j++) { - if (key[i].sk_attno == irel->rd_index->indkey.values[j]) + if (key[i].sk_attno == RelationGetIndex(irel)->indkey.values[j]) { idxkey[i].sk_attno = j + 1; break; @@ -711,7 +711,7 @@ systable_beginscan_ordered(Relation heapRelation, for (j = 0; j < IndexRelationGetNumberOfAttributes(indexRelation); j++) { - if (key[i].sk_attno == indexRelation->rd_index->indkey.values[j]) + if (key[i].sk_attno == RelationGetIndex(indexRelation)->indkey.values[j]) { idxkey[i].sk_attno = j + 1; break; diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 04c97f0c7b2..d3ca10f5617 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -361,7 +361,7 @@ index_beginscan_internal(Relation indexRelation, Relation heapRelation, */ if (index_only_scan) { - int indnkeyatts = indexRelation->rd_index->indnkeyatts; + int indnkeyatts = RelationGetIndex(indexRelation)->indnkeyatts; int namecount = 0; for (int attnum = 0; attnum < indnkeyatts; attnum++) diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index b3032b1f1de..474a6e5bea2 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -141,7 +141,7 @@ _bt_mkscankey(Relation rel, IndexTuple itup) * In NULLS NOT DISTINCT mode, we pretend that there are no null keys, so * that full uniqueness check is done. */ - if (rel->rd_index->indnullsnotdistinct) + if (RelationGetIndex(rel)->indnullsnotdistinct) key->anynullkeys = false; return key; diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index f2ee333f60d..90a3380a7a0 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -126,14 +126,14 @@ GetIndexInputType(Relation index, AttrNumber indexcol) List *indexprs; ListCell *indexpr_item; - Assert(index->rd_index != NULL); - Assert(indexcol > 0 && indexcol <= index->rd_index->indnkeyatts); + Assert(RelationGetIndex(index) != NULL); + Assert(indexcol > 0 && indexcol <= RelationGetIndex(index)->indnkeyatts); opcintype = index->rd_opcintype[indexcol - 1]; if (!IsPolymorphicType(opcintype)) return opcintype; - heapcol = index->rd_index->indkey.values[indexcol - 1]; + heapcol = RelationGetIndex(index)->indkey.values[indexcol - 1]; if (heapcol != 0) /* Simple index column? */ - return getBaseType(get_atttype(index->rd_index->indrelid, heapcol)); + return getBaseType(get_atttype(RelationGetIndex(index)->indrelid, heapcol)); /* * If the index expressions are already cached, skip calling @@ -146,9 +146,9 @@ GetIndexInputType(Relation index, AttrNumber indexcol) else indexprs = RelationGetIndexExpressions(index); indexpr_item = list_head(indexprs); - for (int i = 1; i <= index->rd_index->indnkeyatts; i++) + for (int i = 1; i <= RelationGetIndex(index)->indnkeyatts; i++) { - if (index->rd_index->indkey.values[i - 1] == 0) + if (RelationGetIndex(index)->indkey.values[i - 1] == 0) { /* expression column */ if (indexpr_item == NULL) diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index cf9b88b3e25..a38c398bd66 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -697,7 +697,7 @@ pg_nextoid(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("pg_nextoid() can only be used on system catalogs"))); - if (idx->rd_index->indrelid != RelationGetRelid(rel)) + if (RelationGetIndex(idx)->indrelid != RelationGetRelid(rel)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("index \"%s\" does not belong to table \"%s\"", @@ -721,7 +721,7 @@ pg_nextoid(PG_FUNCTION_ARGS) NameStr(*attname), "oid"))); if (IndexRelationGetNumberOfKeyAttributes(idx) != 1 || - idx->rd_index->indkey.values[0] != attno) + RelationGetIndex(idx)->indkey.values[0] != attno) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("index \"%s\" is not the index for column \"%s\"", diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index d71a8511c6e..393910fd5b2 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1250,7 +1250,7 @@ index_create(Relation heapRelation, else Assert(indexRelation->rd_indexcxt != NULL); - indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs; + RelationGetIndex(indexRelation)->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs; /* Validate opclass-specific options */ if (opclassOptions) @@ -1420,7 +1420,7 @@ index_create_copy(Relation heapRelation, uint16 flags, oldInfo->ii_WithoutOverlaps); /* fetch exclusion constraint info if any */ - if (indexRelation->rd_index->indisexclusion) + if (RelationGetIndex(indexRelation)->indisexclusion) { /* * XXX Beware: we're making newInfo point to oldInfo-owned memory. It @@ -2461,7 +2461,7 @@ IndexInfo * BuildIndexInfo(Relation index) { IndexInfo *ii; - Form_pg_index indexStruct = index->rd_index; + Form_pg_index indexStruct = RelationGetIndex(index); int i; int numAtts; @@ -2521,7 +2521,7 @@ IndexInfo * BuildDummyIndexInfo(Relation index) { IndexInfo *ii; - Form_pg_index indexStruct = index->rd_index; + Form_pg_index indexStruct = RelationGetIndex(index); int i; int numAtts; @@ -2775,8 +2775,8 @@ BuildSpeculativeIndexInfo(Relation index, IndexInfo *ii) bool IsIndexCompatibleAsArbiter(Relation indexRel1, Relation indexRel2) { - Form_pg_index indexForm1 = indexRel1->rd_index; - Form_pg_index indexForm2 = indexRel2->rd_index; + Form_pg_index indexForm1 = RelationGetIndex(indexRel1); + Form_pg_index indexForm2 = RelationGetIndex(indexRel2); /* Only indexes of the same relation can be compared. */ Assert(indexForm1->indrelid == indexForm2->indrelid); diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index fd7d2ec0e3a..bfaec5bf2d7 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -135,7 +135,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple, Assert(indexInfo->ii_Expressions == NIL); Assert(indexInfo->ii_Predicate == NIL); Assert(indexInfo->ii_ExclusionOps == NULL); - Assert(index->rd_index->indimmediate); + Assert(RelationGetIndex(index)->indimmediate); Assert(indexInfo->ii_NumIndexKeyAttrs != 0); /* see earlier check above */ @@ -172,7 +172,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple, isnull, /* is-null flags */ &(heapTuple->t_self), /* tid of heap tuple */ heapRelation, - index->rd_index->indisunique ? + RelationGetIndex(index)->indisunique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO, false, indexInfo); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index b8a0c908637..763a37db586 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1496,7 +1496,7 @@ DefineIndex(ParseState *pstate, createdConstraintId, childRelid); - if (!cldidx->rd_index->indisvalid) + if (!RelationGetIndex(cldidx)->indisvalid) invalidate_parent = true; found = true; @@ -3858,14 +3858,14 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Relation indexRelation = index_open(cellOid, ShareUpdateExclusiveLock); - if (!indexRelation->rd_index->indisvalid) + if (!RelationGetIndex(indexRelation)->indisvalid) ereport(WARNING, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("skipping reindex of invalid index \"%s.%s\"", get_namespace_name(get_rel_namespace(cellOid)), get_rel_name(cellOid)), errhint("Use DROP INDEX or REINDEX INDEX."))); - else if (indexRelation->rd_index->indisexclusion) + else if (RelationGetIndex(indexRelation)->indisexclusion) ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot reindex exclusion constraint index \"%s.%s\" concurrently, skipping", @@ -3911,7 +3911,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Relation indexRelation = index_open(cellOid, ShareUpdateExclusiveLock); - if (!indexRelation->rd_index->indisvalid) + if (!RelationGetIndex(indexRelation)->indisvalid) ereport(WARNING, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("skipping reindex of invalid index \"%s.%s\"", @@ -4088,7 +4088,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid tablespaceid; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); - heapRel = table_open(indexRel->rd_index->indrelid, + heapRel = table_open(RelationGetIndex(indexRel)->indrelid, ShareUpdateExclusiveLock); /* @@ -4132,7 +4132,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein concurrentName = ChooseRelationName(get_rel_name(idx->indexId), NULL, "ccnew", - get_rel_namespace(indexRel->rd_index->indrelid), + get_rel_namespace(RelationGetIndex(indexRel)->indrelid), false); /* Choose the new tablespace, indexes of toast tables are not moved */ @@ -4736,7 +4736,7 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid) ObjectAddressSet(partIdx, RelationRelationId, partRelid); ObjectAddressSet(parentIdx, RelationRelationId, parentOid); ObjectAddressSet(partitionTbl, RelationRelationId, - partitionIdx->rd_index->indrelid); + RelationGetIndex(partitionIdx)->indrelid); recordDependencyOn(&partIdx, &parentIdx, DEPENDENCY_PARTITION_PRI); recordDependencyOn(&partIdx, &partitionTbl, diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index 40748958eaf..a949482d99c 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -727,7 +727,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, indexRel = index_open(indexoid, RowExclusiveLock); if (is_usable_unique_index(indexRel)) { - Form_pg_index indexStruct = indexRel->rd_index; + Form_pg_index indexStruct = RelationGetIndex(indexRel); int indnkeyatts = indexStruct->indnkeyatts; oidvector *indclass; Datum indclassDatum; @@ -904,7 +904,7 @@ refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence) static bool is_usable_unique_index(Relation indexRel) { - Form_pg_index indexStruct = indexRel->rd_index; + Form_pg_index indexStruct = RelationGetIndex(indexRel); /* * Must be unique, valid, immediate, non-partial, and be defined over diff --git a/src/backend/commands/propgraphcmds.c b/src/backend/commands/propgraphcmds.c new file mode 100644 index 00000000000..cabcc90d4f3 --- /dev/null +++ b/src/backend/commands/propgraphcmds.c @@ -0,0 +1,1959 @@ +/*------------------------------------------------------------------------- + * + * propgraphcmds.c + * property graph manipulation + * + * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/backend/commands/propgraphcmds.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/table.h" +#include "access/xact.h" +#include "catalog/catalog.h" +#include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/pg_class.h" +#include "catalog/pg_collation_d.h" +#include "catalog/pg_operator_d.h" +#include "catalog/pg_propgraph_element.h" +#include "catalog/pg_propgraph_element_label.h" +#include "catalog/pg_propgraph_label.h" +#include "catalog/pg_propgraph_label_property.h" +#include "catalog/pg_propgraph_property.h" +#include "commands/defrem.h" +#include "commands/propgraphcmds.h" +#include "commands/tablecmds.h" +#include "miscadmin.h" +#include "nodes/nodeFuncs.h" +#include "parser/parse_coerce.h" +#include "parser/parse_collate.h" +#include "parser/parse_oper.h" +#include "parser/parse_relation.h" +#include "parser/parse_target.h" +#include "utils/acl.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/inval.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/ruleutils.h" +#include "utils/syscache.h" + + +struct element_info +{ + Oid elementid; + char kind; + Oid relid; + char *aliasname; + ArrayType *key; + + char *srcvertex; + Oid srcvertexid; + Oid srcrelid; + ArrayType *srckey; + ArrayType *srcref; + ArrayType *srceqop; + + char *destvertex; + Oid destvertexid; + Oid destrelid; + ArrayType *destkey; + ArrayType *destref; + ArrayType *desteqop; + + List *labels; +}; + + +static ArrayType *propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, + const char *aliasname, int location); +static void propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols, + Relation edge_rel, Relation ref_rel, + const char *aliasname, int location, const char *type, + ArrayType **outkey, ArrayType **outref, ArrayType **outeqop); +static AttrNumber *array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel); +static ArrayType *array_from_attnums(int numattrs, const AttrNumber *attnums); +static Oid insert_element_record(ObjectAddress pgaddress, struct element_info *einfo); +static Oid insert_label_record(Oid graphid, Oid peoid, const char *label); +static void insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties); +static void insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr); +static void check_element_properties(Oid peoid); +static void check_element_label_properties(Oid ellabeloid); +static void check_all_labels_properties(Oid pgrelid); +static Oid get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location); +static Oid get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location); +static Oid get_element_relid(Oid peid); +static List *get_graph_label_ids(Oid graphid); +static List *get_label_element_label_ids(Oid labelid); +static List *get_element_label_property_names(Oid ellabeloid); +static List *get_graph_property_ids(Oid graphid); + + +/* + * CREATE PROPERTY GRAPH + */ +ObjectAddress +CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt) +{ + CreateStmt *cstmt = makeNode(CreateStmt); + char components_persistence; + ListCell *lc; + ObjectAddress pgaddress; + List *vertex_infos = NIL; + List *edge_infos = NIL; + List *element_aliases = NIL; + List *element_oids = NIL; + + if (stmt->pgname->relpersistence == RELPERSISTENCE_UNLOGGED) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("property graphs cannot be unlogged because they do not have storage"))); + + components_persistence = RELPERSISTENCE_PERMANENT; + + foreach(lc, stmt->vertex_tables) + { + PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc); + struct element_info *vinfo; + Relation rel; + + vinfo = palloc0_object(struct element_info); + vinfo->kind = PGEKIND_VERTEX; + + vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); + + rel = table_open(vinfo->relid, NoLock); + + if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) + components_persistence = RELPERSISTENCE_TEMP; + + if (vertex->vtable->alias) + vinfo->aliasname = vertex->vtable->alias->aliasname; + else + vinfo->aliasname = vertex->vtable->relname; + + if (list_member(element_aliases, makeString(vinfo->aliasname))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("alias \"%s\" used more than once as element table", vinfo->aliasname), + parser_errposition(pstate, vertex->location))); + + vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location); + + vinfo->labels = vertex->labels; + + table_close(rel, NoLock); + + vertex_infos = lappend(vertex_infos, vinfo); + + element_aliases = lappend(element_aliases, makeString(vinfo->aliasname)); + } + + foreach(lc, stmt->edge_tables) + { + PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc); + struct element_info *einfo; + Relation rel; + ListCell *lc2; + Oid srcrelid; + Oid destrelid; + Relation srcrel; + Relation destrel; + + einfo = palloc0_object(struct element_info); + einfo->kind = PGEKIND_EDGE; + + einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); + + rel = table_open(einfo->relid, NoLock); + + if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) + components_persistence = RELPERSISTENCE_TEMP; + + if (edge->etable->alias) + einfo->aliasname = edge->etable->alias->aliasname; + else + einfo->aliasname = edge->etable->relname; + + if (list_member(element_aliases, makeString(einfo->aliasname))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("alias \"%s\" used more than once as element table", einfo->aliasname), + parser_errposition(pstate, edge->location))); + + einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location); + + einfo->srcvertex = edge->esrcvertex; + einfo->destvertex = edge->edestvertex; + + srcrelid = 0; + destrelid = 0; + foreach(lc2, vertex_infos) + { + struct element_info *vinfo = lfirst(lc2); + + if (strcmp(vinfo->aliasname, edge->esrcvertex) == 0) + srcrelid = vinfo->relid; + + if (strcmp(vinfo->aliasname, edge->edestvertex) == 0) + destrelid = vinfo->relid; + + if (srcrelid && destrelid) + break; + } + if (!srcrelid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("source vertex \"%s\" of edge \"%s\" does not exist", + edge->esrcvertex, einfo->aliasname), + parser_errposition(pstate, edge->location))); + if (!destrelid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("destination vertex \"%s\" of edge \"%s\" does not exist", + edge->edestvertex, einfo->aliasname), + parser_errposition(pstate, edge->location))); + + srcrel = table_open(srcrelid, NoLock); + destrel = table_open(destrelid, NoLock); + + propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel, + einfo->aliasname, edge->location, "SOURCE", + &einfo->srckey, &einfo->srcref, &einfo->srceqop); + propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel, + einfo->aliasname, edge->location, "DESTINATION", + &einfo->destkey, &einfo->destref, &einfo->desteqop); + + einfo->labels = edge->labels; + + table_close(destrel, NoLock); + table_close(srcrel, NoLock); + + table_close(rel, NoLock); + + edge_infos = lappend(edge_infos, einfo); + + element_aliases = lappend(element_aliases, makeString(einfo->aliasname)); + } + + cstmt->relation = stmt->pgname; + cstmt->oncommit = ONCOMMIT_NOOP; + + /* + * Automatically make it temporary if any component tables are temporary + * (see also DefineView()). + */ + if (stmt->pgname->relpersistence == RELPERSISTENCE_PERMANENT + && components_persistence == RELPERSISTENCE_TEMP) + { + cstmt->relation = copyObject(cstmt->relation); + cstmt->relation->relpersistence = RELPERSISTENCE_TEMP; + ereport(NOTICE, + (errmsg("property graph \"%s\" will be temporary", + stmt->pgname->relname))); + } + + pgaddress = DefineRelation(cstmt, RELKIND_PROPGRAPH, InvalidOid, NULL, NULL); + + foreach(lc, vertex_infos) + { + struct element_info *vinfo = lfirst(lc); + Oid peoid; + + peoid = insert_element_record(pgaddress, vinfo); + element_oids = lappend_oid(element_oids, peoid); + } + + foreach(lc, edge_infos) + { + struct element_info *einfo = lfirst(lc); + Oid peoid; + ListCell *lc2; + + /* + * Look up the vertices again. Now the vertices have OIDs assigned, + * which we need. + */ + foreach(lc2, vertex_infos) + { + struct element_info *vinfo = lfirst(lc2); + + if (strcmp(vinfo->aliasname, einfo->srcvertex) == 0) + { + einfo->srcvertexid = vinfo->elementid; + einfo->srcrelid = vinfo->relid; + } + if (strcmp(vinfo->aliasname, einfo->destvertex) == 0) + { + einfo->destvertexid = vinfo->elementid; + einfo->destrelid = vinfo->relid; + } + if (einfo->srcvertexid && einfo->destvertexid) + break; + } + Assert(einfo->srcvertexid); + Assert(einfo->destvertexid); + Assert(einfo->srcrelid); + Assert(einfo->destrelid); + peoid = insert_element_record(pgaddress, einfo); + element_oids = lappend_oid(element_oids, peoid); + } + + CommandCounterIncrement(); + + foreach_oid(peoid, element_oids) + check_element_properties(peoid); + check_all_labels_properties(pgaddress.objectId); + + return pgaddress; +} + +/* + * Process the key clause specified for an element. If key_clause is non-NIL, + * then it is a list of column names. Otherwise, the primary key of the + * relation is used. The return value is an array of column numbers. + */ +static ArrayType * +propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, const char *aliasname, int location) +{ + ArrayType *a; + + if (key_clause == NIL) + { + Oid pkidx = RelationGetPrimaryKeyIndex(element_rel, false); + + if (!pkidx) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("no key specified and no suitable primary key exists for definition of element \"%s\"", aliasname), + parser_errposition(pstate, location)); + else + { + Relation indexDesc; + + indexDesc = index_open(pkidx, AccessShareLock); + a = array_from_attnums(IndexRelationGetNumberOfKeyAttributes(indexDesc), + RelationGetIndex(indexDesc)->indkey.values); + index_close(indexDesc, NoLock); + } + } + else + { + a = array_from_attnums(list_length(key_clause), + array_from_column_list(pstate, key_clause, location, element_rel)); + } + + return a; +} + +/* + * Process the source or destination link of an edge. + * + * keycols and refcols are column names representing the local and referenced + * (vertex) columns. If they are both NIL, a matching foreign key is looked + * up. + * + * edge_rel and ref_rel are the local and referenced element tables. + * + * aliasname, location, and type are for error messages. type is either + * "SOURCE" or "DESTINATION". + * + * The outputs are arrays of column numbers in outkey and outref. + */ +static void +propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols, + Relation edge_rel, Relation ref_rel, + const char *aliasname, int location, const char *type, + ArrayType **outkey, ArrayType **outref, ArrayType **outeqop) +{ + int nkeys; + AttrNumber *keyattnums; + AttrNumber *refattnums; + Oid *keyeqops; + Datum *datums; + + Assert((keycols && refcols) || (!keycols && !refcols)); + + if (keycols) + { + if (list_length(keycols) != list_length(refcols)) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("mismatching number of columns in %s vertex definition of edge \"%s\"", type, aliasname), + parser_errposition(pstate, location)); + + nkeys = list_length(keycols); + keyattnums = array_from_column_list(pstate, keycols, location, edge_rel); + refattnums = array_from_column_list(pstate, refcols, location, ref_rel); + keyeqops = palloc_array(Oid, nkeys); + + for (int i = 0; i < nkeys; i++) + { + Oid keytype; + int32 keytypmod; + Oid keycoll; + Oid reftype; + int32 reftypmod; + Oid refcoll; + Oid opc; + Oid opf; + StrategyNumber strategy; + + /* + * Lookup equality operator to be used for edge and vertex key. + * Vertex key is equivalent to primary key and edge key is similar + * to foreign key since edge key references vertex key. Hence + * vertex key is used as left operand and edge key is used as + * right operand. The method used to find the equality operators + * is similar to the method used to find equality operators for + * FK/PK comparison in ATAddForeignKeyConstraint() except that + * opclass of the vertex key type is used as a starting point. + * Since we need only equality operators we use both BT and HASH + * strategies. + * + * If the required operators do not exist, we can not construct + * quals linking an edge to its adjacent vertices. + */ + get_atttypetypmodcoll(RelationGetRelid(edge_rel), keyattnums[i], &keytype, &keytypmod, &keycoll); + get_atttypetypmodcoll(RelationGetRelid(ref_rel), refattnums[i], &reftype, &reftypmod, &refcoll); + keyeqops[i] = InvalidOid; + strategy = BTEqualStrategyNumber; + opc = GetDefaultOpClass(reftype, BTREE_AM_OID); + if (!OidIsValid(opc)) + { + opc = GetDefaultOpClass(reftype, HASH_AM_OID); + strategy = HTEqualStrategyNumber; + } + if (OidIsValid(opc)) + { + opf = get_opclass_family(opc); + if (OidIsValid(opf)) + { + keyeqops[i] = get_opfamily_member(opf, reftype, keytype, strategy); + if (!OidIsValid(keyeqops[i])) + { + /* Last resort, implicit cast. */ + if (can_coerce_type(1, &keytype, &reftype, COERCION_IMPLICIT)) + keyeqops[i] = get_opfamily_member(opf, reftype, reftype, strategy); + } + } + } + + if (!OidIsValid(keyeqops[i])) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("no equality operator exists for %s key comparison of edge \"%s\"", + type, aliasname), + parser_errposition(pstate, location)); + + /* + * If collations of key attribute and referenced attribute are + * different, an edge may end up being adjacent to undesired + * vertices. Prohibit such a case. + * + * PK/FK allows different collations as long as they are + * deterministic for backward compatibility. But we can be a bit + * stricter here and follow SQL standard. + */ + if (keycoll != refcoll && + keycoll != DEFAULT_COLLATION_OID && refcoll != DEFAULT_COLLATION_OID && + OidIsValid(keycoll) && OidIsValid(refcoll)) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("collation mismatch in %s key of edge \"%s\": %s vs. %s", + type, aliasname, + get_collation_name(keycoll), get_collation_name(refcoll)), + parser_errposition(pstate, location)); + } + } + else + { + ForeignKeyCacheInfo *fk = NULL; + + foreach_node(ForeignKeyCacheInfo, tmp, RelationGetFKeyList(edge_rel)) + { + if (tmp->confrelid == RelationGetRelid(ref_rel)) + { + if (fk) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("more than one suitable foreign key exists for %s key of edge \"%s\"", type, aliasname), + parser_errposition(pstate, location)); + fk = tmp; + } + } + + if (!fk) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("no %s key specified and no suitable foreign key exists for definition of edge \"%s\"", type, aliasname), + parser_errposition(pstate, location)); + + nkeys = fk->nkeys; + keyattnums = fk->conkey; + refattnums = fk->confkey; + keyeqops = fk->conpfeqop; + } + + *outkey = array_from_attnums(nkeys, keyattnums); + *outref = array_from_attnums(nkeys, refattnums); + datums = palloc_array(Datum, nkeys); + for (int i = 0; i < nkeys; i++) + datums[i] = ObjectIdGetDatum(keyeqops[i]); + *outeqop = construct_array_builtin(datums, nkeys, OIDOID); +} + +/* + * Convert list of column names in the specified relation into an array of + * column numbers. + */ +static AttrNumber * +array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel) +{ + int numattrs; + AttrNumber *attnums; + int i; + ListCell *lc; + + numattrs = list_length(colnames); + attnums = palloc_array(AttrNumber, numattrs); + + i = 0; + foreach(lc, colnames) + { + char *colname = strVal(lfirst(lc)); + Oid relid = RelationGetRelid(element_rel); + AttrNumber attnum; + + attnum = get_attnum(relid, colname); + if (!attnum) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + colname, get_rel_name(relid)), + parser_errposition(pstate, location))); + attnums[i++] = attnum; + } + + for (int j = 0; j < numattrs; j++) + { + for (int k = j + 1; k < numattrs; k++) + { + if (attnums[j] == attnums[k]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("graph key columns list must not contain duplicates"), + parser_errposition(pstate, location))); + } + } + + return attnums; +} + +static ArrayType * +array_from_attnums(int numattrs, const AttrNumber *attnums) +{ + Datum *attnumsd; + + attnumsd = palloc_array(Datum, numattrs); + + for (int i = 0; i < numattrs; i++) + attnumsd[i] = Int16GetDatum(attnums[i]); + + return construct_array_builtin(attnumsd, numattrs, INT2OID); +} + +static void +array_of_attnums_to_objectaddrs(Oid relid, ArrayType *arr, ObjectAddresses *addrs) +{ + Datum *attnumsd; + int numattrs; + + deconstruct_array_builtin(arr, INT2OID, &attnumsd, NULL, &numattrs); + + for (int i = 0; i < numattrs; i++) + { + ObjectAddress referenced; + + ObjectAddressSubSet(referenced, RelationRelationId, relid, DatumGetInt16(attnumsd[i])); + add_exact_object_address(&referenced, addrs); + } +} + +static void +array_of_opers_to_objectaddrs(ArrayType *arr, ObjectAddresses *addrs) +{ + Datum *opersd; + int numopers; + + deconstruct_array_builtin(arr, OIDOID, &opersd, NULL, &numopers); + + for (int i = 0; i < numopers; i++) + { + ObjectAddress referenced; + + ObjectAddressSet(referenced, OperatorRelationId, DatumGetObjectId(opersd[i])); + add_exact_object_address(&referenced, addrs); + } +} + +/* + * Insert a record for an element into the pg_propgraph_element catalog. Also + * inserts labels and properties into their respective catalogs. + */ +static Oid +insert_element_record(ObjectAddress pgaddress, struct element_info *einfo) +{ + Oid graphid = pgaddress.objectId; + Relation rel; + NameData aliasname; + Oid peoid; + Datum values[Natts_pg_propgraph_element] = {0}; + bool nulls[Natts_pg_propgraph_element] = {0}; + HeapTuple tup; + ObjectAddress myself; + ObjectAddress referenced; + ObjectAddresses *addrs; + + rel = table_open(PropgraphElementRelationId, RowExclusiveLock); + + peoid = GetNewOidWithIndex(rel, PropgraphElementObjectIndexId, Anum_pg_propgraph_element_oid); + einfo->elementid = peoid; + values[Anum_pg_propgraph_element_oid - 1] = ObjectIdGetDatum(peoid); + values[Anum_pg_propgraph_element_pgepgid - 1] = ObjectIdGetDatum(graphid); + values[Anum_pg_propgraph_element_pgerelid - 1] = ObjectIdGetDatum(einfo->relid); + namestrcpy(&aliasname, einfo->aliasname); + values[Anum_pg_propgraph_element_pgealias - 1] = NameGetDatum(&aliasname); + values[Anum_pg_propgraph_element_pgekind - 1] = CharGetDatum(einfo->kind); + values[Anum_pg_propgraph_element_pgesrcvertexid - 1] = ObjectIdGetDatum(einfo->srcvertexid); + values[Anum_pg_propgraph_element_pgedestvertexid - 1] = ObjectIdGetDatum(einfo->destvertexid); + values[Anum_pg_propgraph_element_pgekey - 1] = PointerGetDatum(einfo->key); + + if (einfo->srckey) + values[Anum_pg_propgraph_element_pgesrckey - 1] = PointerGetDatum(einfo->srckey); + else + nulls[Anum_pg_propgraph_element_pgesrckey - 1] = true; + if (einfo->srcref) + values[Anum_pg_propgraph_element_pgesrcref - 1] = PointerGetDatum(einfo->srcref); + else + nulls[Anum_pg_propgraph_element_pgesrcref - 1] = true; + if (einfo->srceqop) + values[Anum_pg_propgraph_element_pgesrceqop - 1] = PointerGetDatum(einfo->srceqop); + else + nulls[Anum_pg_propgraph_element_pgesrceqop - 1] = true; + if (einfo->destkey) + values[Anum_pg_propgraph_element_pgedestkey - 1] = PointerGetDatum(einfo->destkey); + else + nulls[Anum_pg_propgraph_element_pgedestkey - 1] = true; + if (einfo->destref) + values[Anum_pg_propgraph_element_pgedestref - 1] = PointerGetDatum(einfo->destref); + else + nulls[Anum_pg_propgraph_element_pgedestref - 1] = true; + if (einfo->desteqop) + values[Anum_pg_propgraph_element_pgedesteqop - 1] = PointerGetDatum(einfo->desteqop); + else + nulls[Anum_pg_propgraph_element_pgedesteqop - 1] = true; + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + CatalogTupleInsert(rel, tup); + heap_freetuple(tup); + + ObjectAddressSet(myself, PropgraphElementRelationId, peoid); + + /* Add dependency on the property graph */ + recordDependencyOn(&myself, &pgaddress, DEPENDENCY_AUTO); + + addrs = new_object_addresses(); + + /* Add dependency on the relation */ + ObjectAddressSet(referenced, RelationRelationId, einfo->relid); + add_exact_object_address(&referenced, addrs); + array_of_attnums_to_objectaddrs(einfo->relid, einfo->key, addrs); + + /* + * Add dependencies on vertices and equality operators used for key + * comparison. + */ + if (einfo->srcvertexid) + { + ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->srcvertexid); + add_exact_object_address(&referenced, addrs); + array_of_attnums_to_objectaddrs(einfo->relid, einfo->srckey, addrs); + array_of_attnums_to_objectaddrs(einfo->srcrelid, einfo->srcref, addrs); + array_of_opers_to_objectaddrs(einfo->srceqop, addrs); + } + if (einfo->destvertexid) + { + ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->destvertexid); + add_exact_object_address(&referenced, addrs); + array_of_attnums_to_objectaddrs(einfo->relid, einfo->destkey, addrs); + array_of_attnums_to_objectaddrs(einfo->destrelid, einfo->destref, addrs); + array_of_opers_to_objectaddrs(einfo->desteqop, addrs); + } + + record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + + table_close(rel, NoLock); + + if (einfo->labels) + { + ListCell *lc; + + foreach(lc, einfo->labels) + { + PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc); + Oid ellabeloid; + + if (lp->label) + ellabeloid = insert_label_record(graphid, peoid, lp->label); + else + ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname); + insert_property_records(graphid, ellabeloid, einfo->relid, lp->properties); + + CommandCounterIncrement(); + } + } + else + { + Oid ellabeloid; + PropGraphProperties *pr = makeNode(PropGraphProperties); + + pr->all = true; + pr->location = -1; + + ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname); + insert_property_records(graphid, ellabeloid, einfo->relid, pr); + } + + return peoid; +} + +/* + * Insert records for a label into the pg_propgraph_label and + * pg_propgraph_element_label catalogs, and register dependencies. + * + * Returns the OID of the new pg_propgraph_element_label record. + */ +static Oid +insert_label_record(Oid graphid, Oid peoid, const char *label) +{ + Oid labeloid; + Oid ellabeloid; + + /* + * Insert into pg_propgraph_label if not already existing. + */ + labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(graphid), CStringGetDatum(label)); + if (!labeloid) + { + Relation rel; + Datum values[Natts_pg_propgraph_label] = {0}; + bool nulls[Natts_pg_propgraph_label] = {0}; + NameData labelname; + HeapTuple tup; + ObjectAddress myself; + ObjectAddress referenced; + + rel = table_open(PropgraphLabelRelationId, RowExclusiveLock); + + labeloid = GetNewOidWithIndex(rel, PropgraphLabelObjectIndexId, Anum_pg_propgraph_label_oid); + values[Anum_pg_propgraph_label_oid - 1] = ObjectIdGetDatum(labeloid); + values[Anum_pg_propgraph_label_pglpgid - 1] = ObjectIdGetDatum(graphid); + namestrcpy(&labelname, label); + values[Anum_pg_propgraph_label_pgllabel - 1] = NameGetDatum(&labelname); + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + CatalogTupleInsert(rel, tup); + heap_freetuple(tup); + + ObjectAddressSet(myself, PropgraphLabelRelationId, labeloid); + + ObjectAddressSet(referenced, RelationRelationId, graphid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + + table_close(rel, NoLock); + } + + /* + * Insert into pg_propgraph_element_label + */ + if (SearchSysCacheExists2(PROPGRAPHELEMENTLABELELEMENTLABEL, + ObjectIdGetDatum(peoid), + ObjectIdGetDatum(labeloid))) + ereport(ERROR, + errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("label \"%s\" already exists", label)); + else + { + Relation rel; + Datum values[Natts_pg_propgraph_element_label] = {0}; + bool nulls[Natts_pg_propgraph_element_label] = {0}; + HeapTuple tup; + ObjectAddress myself; + ObjectAddress referenced; + + rel = table_open(PropgraphElementLabelRelationId, RowExclusiveLock); + + ellabeloid = GetNewOidWithIndex(rel, PropgraphElementLabelObjectIndexId, Anum_pg_propgraph_element_label_oid); + values[Anum_pg_propgraph_element_label_oid - 1] = ObjectIdGetDatum(ellabeloid); + values[Anum_pg_propgraph_element_label_pgellabelid - 1] = ObjectIdGetDatum(labeloid); + values[Anum_pg_propgraph_element_label_pgelelid - 1] = ObjectIdGetDatum(peoid); + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + CatalogTupleInsert(rel, tup); + heap_freetuple(tup); + + ObjectAddressSet(myself, PropgraphElementLabelRelationId, ellabeloid); + + ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + ObjectAddressSet(referenced, PropgraphElementRelationId, peoid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + + table_close(rel, NoLock); + } + + return ellabeloid; +} + +/* + * Insert records for properties into the pg_propgraph_property catalog. + */ +static void +insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties) +{ + List *proplist = NIL; + ParseState *pstate; + ParseNamespaceItem *nsitem; + List *tp; + Relation rel; + ListCell *lc; + + if (properties->all) + { + Relation attRelation; + SysScanDesc scan; + ScanKeyData key[1]; + HeapTuple attributeTuple; + + attRelation = table_open(AttributeRelationId, RowShareLock); + ScanKeyInit(&key[0], + Anum_pg_attribute_attrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(pgerelid)); + scan = systable_beginscan(attRelation, AttributeRelidNumIndexId, + true, NULL, 1, key); + while (HeapTupleIsValid(attributeTuple = systable_getnext(scan))) + { + Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple); + ColumnRef *cr; + ResTarget *rt; + + if (att->attnum <= 0 || att->attisdropped) + continue; + + cr = makeNode(ColumnRef); + rt = makeNode(ResTarget); + + cr->fields = list_make1(makeString(pstrdup(NameStr(att->attname)))); + cr->location = -1; + + rt->name = pstrdup(NameStr(att->attname)); + rt->val = (Node *) cr; + rt->location = -1; + + proplist = lappend(proplist, rt); + } + systable_endscan(scan); + table_close(attRelation, RowShareLock); + } + else + { + proplist = properties->properties; + + foreach(lc, proplist) + { + ResTarget *rt = lfirst_node(ResTarget, lc); + + if (!rt->name && !IsA(rt->val, ColumnRef)) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("property name required"), + parser_errposition(NULL, rt->location)); + } + } + + rel = table_open(pgerelid, AccessShareLock); + + pstate = make_parsestate(NULL); + nsitem = addRangeTableEntryForRelation(pstate, + rel, + AccessShareLock, + NULL, + false, + true); + addNSItemToQuery(pstate, nsitem, true, true, true); + + table_close(rel, NoLock); + + tp = transformTargetList(pstate, proplist, EXPR_KIND_PROPGRAPH_PROPERTY); + if (pstate->p_resolve_unknowns) + resolveTargetListUnknowns(pstate, tp); + assign_expr_collations(pstate, (Node *) tp); + + /* + * When properties are derived from the table's attributes, names are + * already unique. Reject duplicate property names within an explicit + * PROPERTIES clause. Do this after transformTargetList() so that any + * names derived by transformTargetList() are considered. + */ + if (!properties->all) + { + List *seen = NIL; + + foreach_node(TargetEntry, te, tp) + { + String *name = makeString(te->resname); + + if (list_member(seen, name)) + ereport(ERROR, + errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("property \"%s\" specified more than once", te->resname)); + seen = lappend(seen, name); + } + } + + foreach_node(TargetEntry, te, tp) + insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr); +} + +/* + * Insert records for a property into the pg_propgraph_property and + * pg_propgraph_label_property catalogs, and register dependencies. + */ +static void +insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr) +{ + Oid propoid; + Oid exprtypid = exprType((const Node *) expr); + int32 exprtypmod = exprTypmod((const Node *) expr); + Oid exprcollation = exprCollation((const Node *) expr); + + /* + * Insert into pg_propgraph_property if not already existing. + */ + propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, Anum_pg_propgraph_property_oid, ObjectIdGetDatum(graphid), CStringGetDatum(propname)); + if (!OidIsValid(propoid)) + { + Relation rel; + NameData propnamedata; + Datum values[Natts_pg_propgraph_property] = {0}; + bool nulls[Natts_pg_propgraph_property] = {0}; + HeapTuple tup; + ObjectAddress myself; + ObjectAddress referenced; + AclResult aclresult; + + rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock); + + propoid = GetNewOidWithIndex(rel, PropgraphPropertyObjectIndexId, Anum_pg_propgraph_property_oid); + values[Anum_pg_propgraph_property_oid - 1] = ObjectIdGetDatum(propoid); + values[Anum_pg_propgraph_property_pgppgid - 1] = ObjectIdGetDatum(graphid); + namestrcpy(&propnamedata, propname); + values[Anum_pg_propgraph_property_pgpname - 1] = NameGetDatum(&propnamedata); + values[Anum_pg_propgraph_property_pgptypid - 1] = ObjectIdGetDatum(exprtypid); + values[Anum_pg_propgraph_property_pgptypmod - 1] = Int32GetDatum(exprtypmod); + values[Anum_pg_propgraph_property_pgpcollation - 1] = ObjectIdGetDatum(exprcollation); + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + CatalogTupleInsert(rel, tup); + heap_freetuple(tup); + + ObjectAddressSet(myself, PropgraphPropertyRelationId, propoid); + + ObjectAddressSet(referenced, RelationRelationId, graphid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + aclresult = object_aclcheck(TypeRelationId, exprtypid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error_type(aclresult, exprtypid); + ObjectAddressSet(referenced, TypeRelationId, exprtypid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + if (OidIsValid(exprcollation) && exprcollation != DEFAULT_COLLATION_OID) + { + ObjectAddressSet(referenced, CollationRelationId, exprcollation); + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + table_close(rel, NoLock); + } + else + { + HeapTuple pgptup = SearchSysCache1(PROPGRAPHPROPOID, ObjectIdGetDatum(propoid)); + Form_pg_propgraph_property pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup); + Oid proptypid = pgpform->pgptypid; + int32 proptypmod = pgpform->pgptypmod; + Oid propcollation = pgpform->pgpcollation; + + ReleaseSysCache(pgptup); + + /* + * Check that in the graph, all properties with the same name have the + * same type (independent of which label they are on). (See SQL/PGQ + * subclause "Consistency check of a tabular property graph + * descriptor".) + */ + if (proptypid != exprtypid || proptypmod != exprtypmod) + { + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("property \"%s\" data type mismatch: %s vs. %s", + propname, format_type_with_typemod(proptypid, proptypmod), format_type_with_typemod(exprtypid, exprtypmod)), + errdetail("In a property graph, a property of the same name has to have the same data type in each label.")); + } + + /* Similarly for collation */ + if (propcollation != exprcollation) + { + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("property \"%s\" collation mismatch: %s vs. %s", + propname, get_collation_name(propcollation), get_collation_name(exprcollation)), + errdetail("In a property graph, a property of the same name has to have the same collation in each label.")); + } + } + + /* + * Insert into pg_propgraph_label_property + */ + if (SearchSysCacheExists2(PROPGRAPHLABELPROP, ObjectIdGetDatum(ellabeloid), + ObjectIdGetDatum(propoid))) + ereport(ERROR, + errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("property \"%s\" already exists", propname)); + else + { + Relation rel; + Datum values[Natts_pg_propgraph_label_property] = {0}; + bool nulls[Natts_pg_propgraph_label_property] = {0}; + Oid plpoid; + HeapTuple tup; + ObjectAddress myself; + ObjectAddress referenced; + + rel = table_open(PropgraphLabelPropertyRelationId, RowExclusiveLock); + + plpoid = GetNewOidWithIndex(rel, PropgraphLabelPropertyObjectIndexId, Anum_pg_propgraph_label_property_oid); + values[Anum_pg_propgraph_label_property_oid - 1] = ObjectIdGetDatum(plpoid); + values[Anum_pg_propgraph_label_property_plppropid - 1] = ObjectIdGetDatum(propoid); + values[Anum_pg_propgraph_label_property_plpellabelid - 1] = ObjectIdGetDatum(ellabeloid); + values[Anum_pg_propgraph_label_property_plpexpr - 1] = CStringGetTextDatum(nodeToString(expr)); + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + CatalogTupleInsert(rel, tup); + heap_freetuple(tup); + + ObjectAddressSet(myself, PropgraphLabelPropertyRelationId, plpoid); + + ObjectAddressSet(referenced, PropgraphPropertyRelationId, propoid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + + ObjectAddressSet(referenced, PropgraphElementLabelRelationId, ellabeloid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + + CheckUsageOnTypesInSingleRelExpr((Node *) expr, pgerelid, GetUserId()); + recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false); + + table_close(rel, NoLock); + } +} + +/* + * Check that for the given graph element, all properties with the same name + * have the same expression for each label. (See SQL/PGQ subclause "Creation + * of an element table descriptor".) + * + * We check this after all the catalog records are already inserted. This + * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER + * PROPERTY GRAPH. We pass in the element OID so that ALTER PROPERTY GRAPH + * only has to check the element it has just operated on. CREATE PROPERTY + * GRAPH checks all elements it has created. + */ +static void +check_element_properties(Oid peoid) +{ + Relation rel1; + ScanKeyData key1[1]; + SysScanDesc scan1; + HeapTuple tuple1; + List *propoids = NIL; + List *propexprs = NIL; + + rel1 = table_open(PropgraphElementLabelRelationId, AccessShareLock); + ScanKeyInit(&key1[0], + Anum_pg_propgraph_element_label_pgelelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(peoid)); + + scan1 = systable_beginscan(rel1, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, key1); + while (HeapTupleIsValid(tuple1 = systable_getnext(scan1))) + { + Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple1); + Relation rel2; + ScanKeyData key2[1]; + SysScanDesc scan2; + HeapTuple tuple2; + + rel2 = table_open(PropgraphLabelPropertyRelationId, AccessShareLock); + ScanKeyInit(&key2[0], + Anum_pg_propgraph_label_property_plpellabelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ellabel->oid)); + + scan2 = systable_beginscan(rel2, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key2); + while (HeapTupleIsValid(tuple2 = systable_getnext(scan2))) + { + Form_pg_propgraph_label_property lprop = (Form_pg_propgraph_label_property) GETSTRUCT(tuple2); + Oid propoid; + Datum datum; + bool isnull; + char *propexpr; + ListCell *lc1, + *lc2; + bool found; + + propoid = lprop->plppropid; + datum = heap_getattr(tuple2, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(rel2), &isnull); + Assert(!isnull); + propexpr = TextDatumGetCString(datum); + + found = false; + forboth(lc1, propoids, lc2, propexprs) + { + if (propoid == lfirst_oid(lc1)) + { + Node *na, + *nb; + + na = stringToNode(propexpr); + nb = stringToNode(lfirst(lc2)); + + found = true; + + if (!equal(na, nb)) + { + HeapTuple tuple3; + Form_pg_propgraph_element elform; + List *dpcontext; + char *dpa, + *dpb; + + tuple3 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peoid)); + if (!tuple3) + elog(ERROR, "cache lookup failed for property graph element %u", peoid); + elform = (Form_pg_propgraph_element) GETSTRUCT(tuple3); + dpcontext = deparse_context_for(get_rel_name(elform->pgerelid), elform->pgerelid); + + dpa = deparse_expression(na, dpcontext, false, false); + dpb = deparse_expression(nb, dpcontext, false, false); + + /* + * show in sorted order to keep output independent of + * index order + */ + if (strcmp(dpa, dpb) > 0) + { + char *tmp; + + tmp = dpa; + dpa = dpb; + dpb = tmp; + } + + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("element \"%s\" property \"%s\" expression mismatch: %s vs. %s", + NameStr(elform->pgealias), get_propgraph_property_name(propoid), dpa, dpb), + errdetail("In a property graph element, a property of the same name has to have the same expression in each label.")); + + ReleaseSysCache(tuple3); + } + + break; + } + } + + if (!found) + { + propoids = lappend_oid(propoids, propoid); + propexprs = lappend(propexprs, propexpr); + } + } + systable_endscan(scan2); + table_close(rel2, AccessShareLock); + } + + systable_endscan(scan1); + table_close(rel1, AccessShareLock); +} + +/* + * Check that for the given element label, all labels of the same name in the + * graph have the same number and names of properties (independent of which + * element they are on). (See SQL/PGQ subclause "Consistency check of a + * tabular property graph descriptor".) + * + * We check this after all the catalog records are already inserted. This + * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER + * PROPERTY GRAPH. We pass in the element label OID so that some variants of + * ALTER PROPERTY GRAPH only have to check the element label it has just + * operated on. CREATE PROPERTY GRAPH and other ALTER PROPERTY GRAPH variants + * check all labels. + */ +static void +check_element_label_properties(Oid ellabeloid) +{ + Relation rel; + SysScanDesc scan; + ScanKeyData key[1]; + HeapTuple tuple; + Oid labelid = InvalidOid; + Oid ref_ellabeloid = InvalidOid; + List *myprops, + *refprops; + List *diff1, + *diff2; + + rel = table_open(PropgraphElementLabelRelationId, AccessShareLock); + + /* + * Get element label info + */ + ScanKeyInit(&key[0], + Anum_pg_propgraph_element_label_oid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(ellabeloid)); + scan = systable_beginscan(rel, PropgraphElementLabelObjectIndexId, true, NULL, 1, key); + if (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple); + + labelid = ellabel->pgellabelid; + } + systable_endscan(scan); + if (!labelid) + elog(ERROR, "element label %u not found", ellabeloid); + + /* + * Find a reference element label to fetch label properties. The + * reference element label has to have the same label OID as the one being + * checked but a different element OID. + */ + ScanKeyInit(&key[0], + Anum_pg_propgraph_element_label_pgellabelid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(labelid)); + scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + Form_pg_propgraph_element_label otherellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple); + + if (otherellabel->oid != ellabeloid) + { + ref_ellabeloid = otherellabel->oid; + break; + } + } + systable_endscan(scan); + + table_close(rel, AccessShareLock); + + /* + * If there is no previous definition of this label, then we are done. + */ + if (!ref_ellabeloid) + return; + + /* + * Now check number and names. + * + * XXX We could provide more detail in the error messages, but that would + * probably only be useful for some ALTER commands, because otherwise it's + * not really clear which label definition is the wrong one, and so you'd + * have to construct a rather verbose report to be of any use. Let's keep + * it simple for now. + */ + + myprops = get_element_label_property_names(ellabeloid); + refprops = get_element_label_property_names(ref_ellabeloid); + + if (list_length(refprops) != list_length(myprops)) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("mismatching number of properties in definition of label \"%s\"", get_propgraph_label_name(labelid))); + + diff1 = list_difference(myprops, refprops); + diff2 = list_difference(refprops, myprops); + + if (diff1 || diff2) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("mismatching property names in definition of label \"%s\"", get_propgraph_label_name(labelid))); +} + +/* + * As above, but check all labels of a graph. + */ +static void +check_all_labels_properties(Oid pgrelid) +{ + foreach_oid(labeloid, get_graph_label_ids(pgrelid)) + { + foreach_oid(ellabeloid, get_label_element_label_ids(labeloid)) + { + check_element_label_properties(ellabeloid); + } + } +} + +/* + * ALTER PROPERTY GRAPH + */ +ObjectAddress +AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt) +{ + Oid pgrelid; + ListCell *lc; + ObjectAddress pgaddress; + + /* + * ShareRowExclusiveLock is required because this command runs some + * graph-wide consistency checks that wouldn't work if more than one ALTER + * PROPERTY GRAPH could operate on the same graph at once. + */ + pgrelid = RangeVarGetRelidExtended(stmt->pgname, + ShareRowExclusiveLock, + stmt->missing_ok ? RVR_MISSING_OK : 0, + RangeVarCallbackOwnsRelation, + NULL); + if (pgrelid == InvalidOid) + { + ereport(NOTICE, + (errmsg("relation \"%s\" does not exist, skipping", + stmt->pgname->relname))); + return InvalidObjectAddress; + } + + ObjectAddressSet(pgaddress, RelationRelationId, pgrelid); + + foreach(lc, stmt->add_vertex_tables) + { + PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc); + struct element_info *vinfo; + Relation rel; + Oid peoid; + + vinfo = palloc0_object(struct element_info); + vinfo->kind = PGEKIND_VERTEX; + + vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); + + rel = table_open(vinfo->relid, NoLock); + + if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot add temporary element table to non-temporary property graph"), + errdetail("Table \"%s\" is a temporary table.", get_rel_name(vinfo->relid)), + parser_errposition(pstate, vertex->vtable->location))); + + if (vertex->vtable->alias) + vinfo->aliasname = vertex->vtable->alias->aliasname; + else + vinfo->aliasname = vertex->vtable->relname; + + vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location); + + vinfo->labels = vertex->labels; + + table_close(rel, NoLock); + + if (SearchSysCacheExists2(PROPGRAPHELALIAS, + ObjectIdGetDatum(pgrelid), + CStringGetDatum(vinfo->aliasname))) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("alias \"%s\" already exists in property graph \"%s\"", + vinfo->aliasname, stmt->pgname->relname), + parser_errposition(pstate, vertex->vtable->location)); + + peoid = insert_element_record(pgaddress, vinfo); + + CommandCounterIncrement(); + check_element_properties(peoid); + check_all_labels_properties(pgrelid); + } + + foreach(lc, stmt->add_edge_tables) + { + PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc); + struct element_info *einfo; + Relation rel; + Relation srcrel; + Relation destrel; + Oid peoid; + + einfo = palloc0_object(struct element_info); + einfo->kind = PGEKIND_EDGE; + + einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); + + rel = table_open(einfo->relid, NoLock); + + if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot add temporary element table to non-temporary property graph"), + errdetail("Table \"%s\" is a temporary table.", get_rel_name(einfo->relid)), + parser_errposition(pstate, edge->etable->location))); + + if (edge->etable->alias) + einfo->aliasname = edge->etable->alias->aliasname; + else + einfo->aliasname = edge->etable->relname; + + einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location); + + einfo->srcvertexid = get_vertex_oid(pstate, pgrelid, edge->esrcvertex, edge->location); + einfo->destvertexid = get_vertex_oid(pstate, pgrelid, edge->edestvertex, edge->location); + + einfo->srcrelid = get_element_relid(einfo->srcvertexid); + einfo->destrelid = get_element_relid(einfo->destvertexid); + + srcrel = table_open(einfo->srcrelid, AccessShareLock); + destrel = table_open(einfo->destrelid, AccessShareLock); + + propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel, + einfo->aliasname, edge->location, "SOURCE", + &einfo->srckey, &einfo->srcref, &einfo->srceqop); + propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel, + einfo->aliasname, edge->location, "DESTINATION", + &einfo->destkey, &einfo->destref, &einfo->desteqop); + + einfo->labels = edge->labels; + + table_close(destrel, NoLock); + table_close(srcrel, NoLock); + + table_close(rel, NoLock); + + if (SearchSysCacheExists2(PROPGRAPHELALIAS, + ObjectIdGetDatum(pgrelid), + CStringGetDatum(einfo->aliasname))) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("alias \"%s\" already exists in property graph \"%s\"", + einfo->aliasname, stmt->pgname->relname), + parser_errposition(pstate, edge->etable->location)); + + peoid = insert_element_record(pgaddress, einfo); + + CommandCounterIncrement(); + check_element_properties(peoid); + check_all_labels_properties(pgrelid); + } + + foreach(lc, stmt->drop_vertex_tables) + { + char *alias = strVal(lfirst(lc)); + Oid peoid; + ObjectAddress obj; + + peoid = get_vertex_oid(pstate, pgrelid, alias, -1); + ObjectAddressSet(obj, PropgraphElementRelationId, peoid); + performDeletion(&obj, stmt->drop_behavior, 0); + } + + foreach(lc, stmt->drop_edge_tables) + { + char *alias = strVal(lfirst(lc)); + Oid peoid; + ObjectAddress obj; + + peoid = get_edge_oid(pstate, pgrelid, alias, -1); + ObjectAddressSet(obj, PropgraphElementRelationId, peoid); + performDeletion(&obj, stmt->drop_behavior, 0); + } + + /* Remove any orphaned pg_propgraph_label entries */ + if (stmt->drop_vertex_tables || stmt->drop_edge_tables) + { + foreach_oid(labeloid, get_graph_label_ids(pgrelid)) + { + if (!get_label_element_label_ids(labeloid)) + { + ObjectAddress obj; + + ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid); + performDeletion(&obj, stmt->drop_behavior, 0); + } + } + } + + foreach(lc, stmt->add_labels) + { + PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc); + Oid peoid; + Oid pgerelid; + Oid ellabeloid; + + Assert(lp->label); + + if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) + peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); + else + peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); + + pgerelid = get_element_relid(peoid); + + ellabeloid = insert_label_record(pgrelid, peoid, lp->label); + insert_property_records(pgrelid, ellabeloid, pgerelid, lp->properties); + + CommandCounterIncrement(); + check_element_properties(peoid); + check_element_label_properties(ellabeloid); + } + + if (stmt->drop_label) + { + Oid peoid; + Oid labeloid; + Oid ellabeloid = InvalidOid; + ObjectAddress obj; + Relation ellabelrel; + SysScanDesc ellabelscan; + ScanKeyData ellabelkey[1]; + int nlabels; + HeapTuple tuple; + + if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) + peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); + else + peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); + + labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, + Anum_pg_propgraph_label_oid, + ObjectIdGetDatum(pgrelid), + CStringGetDatum(stmt->drop_label)); + if (!labeloid) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", + get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label), + parser_errposition(pstate, -1)); + + /* + * Is the given label associated with the element? Is this the only + * label associated with the element? Scan the + * pg_propgraph_element_label table to find answers to these + * questions. Stop scanning when we know both answers. + */ + ellabelrel = table_open(PropgraphElementLabelRelationId, AccessShareLock); + ScanKeyInit(&ellabelkey[0], + Anum_pg_propgraph_element_label_pgelelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(peoid)); + ellabelscan = systable_beginscan(ellabelrel, PropgraphElementLabelElementLabelIndexId, + true, NULL, 1, ellabelkey); + nlabels = 0; + while (HeapTupleIsValid(tuple = systable_getnext(ellabelscan))) + { + Form_pg_propgraph_element_label ellabelform = (Form_pg_propgraph_element_label) GETSTRUCT(tuple); + + nlabels++; + + if (ellabelform->pgellabelid == labeloid) + ellabeloid = ellabelform->oid; + + if (nlabels > 1 && ellabeloid) + break; + } + systable_endscan(ellabelscan); + table_close(ellabelrel, AccessShareLock); + + if (!ellabeloid) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", + get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label), + parser_errposition(pstate, -1)); + + /* + * Prevent dropping the last label from an element. Every element must + * have at least one label associated with it. + */ + if (nlabels == 1) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot drop the last label from element \"%s\"", + stmt->element_alias), + errhint("Every element must have at least one label."))); + + ObjectAddressSet(obj, PropgraphElementLabelRelationId, ellabeloid); + performDeletion(&obj, stmt->drop_behavior, 0); + + /* Remove any orphaned pg_propgraph_label entries */ + if (!get_label_element_label_ids(labeloid)) + { + ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid); + performDeletion(&obj, stmt->drop_behavior, 0); + } + } + + if (stmt->add_properties) + { + Oid peoid; + Oid pgerelid; + Oid labeloid; + Oid ellabeloid = InvalidOid; + + if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) + peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); + else + peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); + + labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, + Anum_pg_propgraph_label_oid, + ObjectIdGetDatum(pgrelid), + CStringGetDatum(stmt->alter_label)); + if (labeloid) + ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL, + Anum_pg_propgraph_element_label_oid, + ObjectIdGetDatum(peoid), + ObjectIdGetDatum(labeloid)); + if (!ellabeloid) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", + get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label), + parser_errposition(pstate, -1)); + + pgerelid = get_element_relid(peoid); + + insert_property_records(pgrelid, ellabeloid, pgerelid, stmt->add_properties); + + CommandCounterIncrement(); + check_element_properties(peoid); + check_element_label_properties(ellabeloid); + } + + if (stmt->drop_properties) + { + Oid peoid; + Oid labeloid; + Oid ellabeloid = InvalidOid; + ObjectAddress obj; + + if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) + peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); + else + peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); + + labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, + Anum_pg_propgraph_label_oid, + ObjectIdGetDatum(pgrelid), + CStringGetDatum(stmt->alter_label)); + if (labeloid) + ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL, + Anum_pg_propgraph_element_label_oid, + ObjectIdGetDatum(peoid), + ObjectIdGetDatum(labeloid)); + + if (!ellabeloid) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", + get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label), + parser_errposition(pstate, -1)); + + foreach(lc, stmt->drop_properties) + { + char *propname = strVal(lfirst(lc)); + Oid propoid; + Oid plpoid = InvalidOid; + + propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, + Anum_pg_propgraph_property_oid, + ObjectIdGetDatum(pgrelid), + CStringGetDatum(propname)); + if (propoid) + plpoid = GetSysCacheOid2(PROPGRAPHLABELPROP, + Anum_pg_propgraph_label_property_oid, + ObjectIdGetDatum(ellabeloid), + ObjectIdGetDatum(propoid)); + if (!plpoid) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("property graph \"%s\" element \"%s\" label \"%s\" has no property \"%s\"", + get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label, propname), + parser_errposition(pstate, -1)); + + ObjectAddressSet(obj, PropgraphLabelPropertyRelationId, plpoid); + performDeletion(&obj, stmt->drop_behavior, 0); + } + + check_element_label_properties(ellabeloid); + } + + /* Remove any orphaned pg_propgraph_property entries */ + if (stmt->drop_properties || stmt->drop_vertex_tables || stmt->drop_edge_tables || stmt->drop_label) + { + foreach_oid(propoid, get_graph_property_ids(pgrelid)) + { + Relation rel; + SysScanDesc scan; + ScanKeyData key[1]; + + rel = table_open(PropgraphLabelPropertyRelationId, RowShareLock); + ScanKeyInit(&key[0], + Anum_pg_propgraph_label_property_plppropid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(propoid)); + /* XXX no suitable index */ + scan = systable_beginscan(rel, InvalidOid, true, NULL, 1, key); + if (!systable_getnext(scan)) + { + ObjectAddress obj; + + ObjectAddressSet(obj, PropgraphPropertyRelationId, propoid); + performDeletion(&obj, stmt->drop_behavior, 0); + } + + systable_endscan(scan); + table_close(rel, RowShareLock); + } + } + + /* + * Invalidate relcache entry of the property graph so that the queries in + * the cached plans referencing the property graph will be rewritten + * considering changes to the property graph. + */ + CacheInvalidateRelcacheByRelid(pgrelid); + + return pgaddress; +} + +/* + * Get OID of vertex from graph OID and element alias. Element must be a + * vertex, otherwise error. + */ +static Oid +get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location) +{ + HeapTuple tuple; + Oid peoid; + + tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias)); + if (!tuple) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("property graph \"%s\" has no element with alias \"%s\"", + get_rel_name(pgrelid), alias), + parser_errposition(pstate, location)); + + if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_VERTEX) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("element \"%s\" of property graph \"%s\" is not a vertex", + alias, get_rel_name(pgrelid)), + parser_errposition(pstate, location)); + + peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid; + + ReleaseSysCache(tuple); + + return peoid; +} + +/* + * Get OID of edge from graph OID and element alias. Element must be an edge, + * otherwise error. + */ +static Oid +get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location) +{ + HeapTuple tuple; + Oid peoid; + + tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias)); + if (!tuple) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("property graph \"%s\" has no element with alias \"%s\"", + get_rel_name(pgrelid), alias), + parser_errposition(pstate, location)); + + if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_EDGE) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("element \"%s\" of property graph \"%s\" is not an edge", + alias, get_rel_name(pgrelid)), + parser_errposition(pstate, location)); + + peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid; + + ReleaseSysCache(tuple); + + return peoid; +} + +/* + * Get the element table relation OID from the OID of the element. + */ +static Oid +get_element_relid(Oid peid) +{ + HeapTuple tuple; + Oid pgerelid; + + tuple = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peid)); + if (!tuple) + elog(ERROR, "cache lookup failed for property graph element %u", peid); + + pgerelid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgerelid; + + ReleaseSysCache(tuple); + + return pgerelid; +} + +/* + * Get a list of all label OIDs of a graph. + */ +static List * +get_graph_label_ids(Oid graphid) +{ + Relation rel; + SysScanDesc scan; + ScanKeyData key[1]; + HeapTuple tuple; + List *result = NIL; + + rel = table_open(PropgraphLabelRelationId, AccessShareLock); + ScanKeyInit(&key[0], + Anum_pg_propgraph_label_pglpgid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(graphid)); + scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId, true, NULL, 1, key); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + result = lappend_oid(result, ((Form_pg_propgraph_label) GETSTRUCT(tuple))->oid); + } + systable_endscan(scan); + table_close(rel, AccessShareLock); + + return result; +} + +/* + * Get a list of all element label OIDs for a label. + */ +static List * +get_label_element_label_ids(Oid labelid) +{ + Relation rel; + SysScanDesc scan; + ScanKeyData key[1]; + HeapTuple tuple; + List *result = NIL; + + rel = table_open(PropgraphElementLabelRelationId, AccessShareLock); + ScanKeyInit(&key[0], + Anum_pg_propgraph_element_label_pgellabelid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(labelid)); + scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + result = lappend_oid(result, ((Form_pg_propgraph_element_label) GETSTRUCT(tuple))->oid); + } + systable_endscan(scan); + table_close(rel, AccessShareLock); + + return result; +} + +/* + * Get the names of properties associated with the given element label OID. + * + * The result is a list of String nodes (so we can use list functions to + * detect differences). + */ +static List * +get_element_label_property_names(Oid ellabeloid) +{ + Relation rel; + SysScanDesc scan; + ScanKeyData key[1]; + HeapTuple tuple; + List *result = NIL; + + rel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_propgraph_label_property_plpellabelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ellabeloid)); + + scan = systable_beginscan(rel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key); + + while ((tuple = systable_getnext(scan))) + { + Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tuple); + + result = lappend(result, makeString(get_propgraph_property_name(plpform->plppropid))); + } + + systable_endscan(scan); + table_close(rel, AccessShareLock); + + return result; +} + +/* + * Get a list of all property OIDs of a graph. + */ +static List * +get_graph_property_ids(Oid graphid) +{ + Relation rel; + SysScanDesc scan; + ScanKeyData key[1]; + HeapTuple tuple; + List *result = NIL; + + rel = table_open(PropgraphPropertyRelationId, AccessShareLock); + ScanKeyInit(&key[0], + Anum_pg_propgraph_property_pgppgid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(graphid)); + scan = systable_beginscan(rel, PropgraphPropertyNameIndexId, true, NULL, 1, key); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + result = lappend_oid(result, ((Form_pg_propgraph_property) GETSTRUCT(tuple))->oid); + } + systable_endscan(scan); + table_close(rel, AccessShareLock); + + return result; +} diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 759be53d6b8..03ab7a6b614 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -624,7 +624,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, */ if (cmd != REPACK_COMMAND_CLUSTER && !allowSystemTableMods && OidIsValid(indexOid) && - IsCatalogRelation(OldHeap) && !index->rd_index->indisclustered) + IsCatalogRelation(OldHeap) && !RelationGetIndex(index)->indisclustered) ereport(ERROR, errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", @@ -773,8 +773,8 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode) /* * Check that index is in fact an index on the given relation */ - if (OldIndex->rd_index == NULL || - OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap)) + if (RelationGetIndex(OldIndex) == NULL || + RelationGetIndex(OldIndex)->indrelid != RelationGetRelid(OldHeap)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index for table \"%s\"", @@ -808,7 +808,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode) * might put recently-dead tuples out-of-order in the new table, and there * is little harm in that.) */ - if (!OldIndex->rd_index->indisvalid) + if (!RelationGetIndex(OldIndex)->indisvalid) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot cluster on invalid index \"%s\"", @@ -3012,7 +3012,7 @@ static bool find_target_tuple(Relation rel, ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *retrieved) { - Form_pg_index idx = chgcxt->cc_ident_index->rd_index; + Form_pg_index idx = RelationGetIndex(chgcxt->cc_ident_index); IndexScanDesc scan; bool retval = false; @@ -3071,7 +3071,7 @@ identity_key_equal(ChangeContext *chgcxt, TupleTableSlot *locator, for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++) { ScanKey entry = &chgcxt->cc_ident_key[i]; - AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i]; + AttrNumber attno = RelationGetIndex(chgcxt->cc_ident_index)->indkey.values[i]; Assert(attno > 0); @@ -3242,7 +3242,7 @@ initialize_change_context(ChangeContext *chgcxt, { Form_pg_index indexForm; - indexForm = chgcxt->cc_ident_index->rd_index; + indexForm = RelationGetIndex(chgcxt->cc_ident_index); chgcxt->cc_ident_key_nentries = indexForm->indnkeyatts; chgcxt->cc_ident_key = (ScanKey) palloc_array(ScanKeyData, indexForm->indnkeyatts); for (int i = 0; i < indexForm->indnkeyatts; i++) @@ -3286,7 +3286,7 @@ initialize_change_context(ChangeContext *chgcxt, chgcxt->cc_last_key_attno = InvalidAttrNumber; for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++) { - AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i]; + AttrNumber attno = RelationGetIndex(chgcxt->cc_ident_index)->indkey.values[i]; Assert(attno > 0); chgcxt->cc_last_key_attno = Max(chgcxt->cc_last_key_attno, attno); @@ -3551,7 +3551,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes) newName = ChooseRelationName(get_rel_name(oldindex), NULL, "repacknew", - get_rel_namespace(ind->rd_index->indrelid), + get_rel_namespace(RelationGetIndex(ind)->indrelid), false); newindex = index_create_copy(NewHeap, INDEX_CREATE_SUPPRESS_PROGRESS, oldindex, ind->rd_rel->reltablespace, @@ -3599,7 +3599,7 @@ copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id) ScanKeyInit(&skey, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(old_index->rd_index->indrelid)); + ObjectIdGetDatum(RelationGetIndex(old_index)->indrelid)); scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true, NULL, 1, &skey); desc = RelationGetDescr(rel); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 2f073ddb84a..dc43eb5a610 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1306,7 +1306,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { - if (idxRel->rd_index->indisunique) + if (RelationGetIndex(idxRel)->indisunique) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot create foreign partition of partitioned table \"%s\"", @@ -9075,12 +9075,12 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa if (rel->rd_rel->relkind == RELKIND_INDEX || rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) { - if (attnum > rel->rd_index->indnkeyatts) + if (attnum > RelationGetIndex(rel)->indnkeyatts) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"", NameStr(attrtuple->attname), RelationGetRelationName(rel)))); - else if (rel->rd_index->indkey.values[attnum - 1] != 0) + else if (RelationGetIndex(rel)->indkey.values[attnum - 1] != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"", @@ -9215,9 +9215,9 @@ SetIndexStorageProperties(Relation rel, Relation attrelation, indrel = index_open(indexoid, lockmode); - for (int i = 0; i < indrel->rd_index->indnatts; i++) + for (int i = 0; i < RelationGetIndex(indrel)->indnatts; i++) { - if (indrel->rd_index->indkey.values[i] == attnum) + if (RelationGetIndex(indrel)->indkey.values[i] == attnum) { indattnum = i + 1; break; @@ -14620,8 +14620,8 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha Relation pk = relation_open(pkindex, AccessShareLock); pkattrs = NULL; - for (int i = 0; i < pk->rd_index->indnkeyatts; i++) - pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]); + for (int i = 0; i < RelationGetIndex(pk)->indnkeyatts; i++) + pkattrs = bms_add_member(pkattrs, RelationGetIndex(pk)->indkey.values[i]); relation_close(pk, AccessShareLock); } @@ -19193,8 +19193,8 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode indexRel = index_open(indexOid, ShareLock); /* Check that the index is on the relation we're altering. */ - if (indexRel->rd_index == NULL || - indexRel->rd_index->indrelid != RelationGetRelid(rel)) + if (RelationGetIndex(indexRel) == NULL || + RelationGetIndex(indexRel)->indrelid != RelationGetRelid(rel)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index for table \"%s\"", @@ -19207,14 +19207,14 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode * exclusion), we can use that too. */ if ((!indexRel->rd_indam->amcanunique || - !indexRel->rd_index->indisunique) && - !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion)) + !RelationGetIndex(indexRel)->indisunique) && + !(RelationGetIndex(indexRel)->indisunique && RelationGetIndex(indexRel)->indisexclusion)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot use non-unique index \"%s\" as replica identity", RelationGetRelationName(indexRel)))); /* Deferred indexes are not guaranteed to be always unique. */ - if (!indexRel->rd_index->indimmediate) + if (!RelationGetIndex(indexRel)->indimmediate) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use non-immediate index \"%s\" as replica identity", @@ -19235,7 +19235,7 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode /* Check index for nullable columns. */ for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++) { - int16 attno = indexRel->rd_index->indkey.values[key]; + int16 attno = RelationGetIndex(indexRel)->indkey.values[key]; Form_pg_attribute attr; HeapTuple contup; Form_pg_constraint conForm; @@ -21362,8 +21362,8 @@ AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel) Oid idx = lfirst_oid(cell); Relation idxRel = index_open(idx, AccessShareLock); - if (idxRel->rd_index->indisunique || - idxRel->rd_index->indisprimary) + if (RelationGetIndex(idxRel)->indisunique || + RelationGetIndex(idxRel)->indisprimary) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"", @@ -21422,7 +21422,7 @@ AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel) continue; /* If this index is invalid, can't use it */ - if (!attachrelIdxRels[i]->rd_index->indisvalid) + if (!RelationGetIndex(attachrelIdxRels[i])->indisvalid) continue; if (CompareIndexInfo(attachInfos[i], info, @@ -22346,7 +22346,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) * partition, so lock that one too. */ state.partitionOid = InvalidOid; - state.parentTblOid = parentIdx->rd_index->indrelid; + state.parentTblOid = RelationGetIndex(parentIdx)->indrelid; state.lockedParentTbl = false; partIdxId = RangeVarGetRelidExtended(name, AccessExclusiveLock, 0, @@ -22362,8 +22362,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) partIdx = relation_open(partIdxId, AccessExclusiveLock); /* we already hold locks on both tables, so this is safe: */ - parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock); - partTbl = relation_open(partIdx->rd_index->indrelid, NoLock); + parentTbl = relation_open(RelationGetIndex(parentIdx)->indrelid, AccessShareLock); + partTbl = relation_open(RelationGetIndex(partIdx)->indrelid, NoLock); ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx)); @@ -22466,7 +22466,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) * If it's a primary key, make sure the columns in the partition are * NOT NULL. */ - if (parentIdx->rd_index->indisprimary) + if (RelationGetIndex(parentIdx)->indisprimary) verifyPartitionIndexNotNull(childInfo, partTbl); /* All good -- do it */ @@ -22479,7 +22479,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) validatePartitionedIndex(parentIdx, parentTbl); } - else if (!parentIdx->rd_index->indisvalid) + else if (!RelationGetIndex(parentIdx)->indisvalid) { /* * The index is attached, but the parent is still invalid; see if it @@ -22612,7 +22612,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl) parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false); parentIdx = relation_open(parentIdxId, AccessExclusiveLock); parentTbl = relation_open(parentTblId, AccessExclusiveLock); - Assert(!parentIdx->rd_index->indisvalid); + Assert(!RelationGetIndex(parentIdx)->indisvalid); validatePartitionedIndex(parentIdx, parentTbl); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 161f50ea560..a14dac060e3 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -2441,7 +2441,7 @@ vac_open_indexes(Relation relation, LOCKMODE lockmode, Relation indrel; indrel = index_open(indexoid, lockmode); - if (indrel->rd_index->indisready) + if (RelationGetIndex(indrel)->indisready) (*Irel)[i++] = indrel; else index_close(indrel, lockmode); diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index d0a714a42f2..98ffe673555 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -219,7 +219,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative) * If the indexes are to be used for speculative insertion, add extra * information required by unique index entries. */ - if (speculative && ii->ii_Unique && !indexDesc->rd_index->indisexclusion) + if (speculative && ii->ii_Unique && !RelationGetIndex(indexDesc)->indisexclusion) BuildSpeculativeIndexInfo(indexDesc, ii); relationDescs[i] = indexDesc; @@ -411,7 +411,7 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, applyNoDupErr = (flags & EIIT_NO_DUPE_ERROR) && (arbiterIndexes == NIL || list_member_oid(arbiterIndexes, - indexRelation->rd_index->indexrelid)); + RelationGetIndex(indexRelation)->indexrelid)); /* * The index AM does the actual insertion, plus uniqueness checking. @@ -426,11 +426,11 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, * For a speculative insertion (used by INSERT ... ON CONFLICT), do * the same as for a deferrable unique index. */ - if (!indexRelation->rd_index->indisunique) + if (!RelationGetIndex(indexRelation)->indisunique) checkUnique = UNIQUE_CHECK_NO; else if (applyNoDupErr) checkUnique = UNIQUE_CHECK_PARTIAL; - else if (indexRelation->rd_index->indimmediate) + else if (RelationGetIndex(indexRelation)->indimmediate) checkUnique = UNIQUE_CHECK_YES; else checkUnique = UNIQUE_CHECK_PARTIAL; @@ -480,7 +480,7 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, violationOK = true; waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT; } - else if (!indexRelation->rd_index->indimmediate) + else if (!RelationGetIndex(indexRelation)->indimmediate) { violationOK = true; waitMode = CEOUC_NOWAIT; @@ -510,7 +510,7 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, * speculative conflict, since that always requires a restart. */ result = lappend_oid(result, RelationGetRelid(indexRelation)); - if (indexRelation->rd_index->indimmediate && specConflict) + if (RelationGetIndex(indexRelation)->indimmediate && specConflict) *specConflict = true; } } @@ -600,10 +600,10 @@ ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, /* When specific arbiter indexes requested, only examine them */ if (arbiterIndexes != NIL && !list_member_oid(arbiterIndexes, - indexRelation->rd_index->indexrelid)) + RelationGetIndex(indexRelation)->indexrelid)) continue; - if (!indexRelation->rd_index->indimmediate) + if (!RelationGetIndex(indexRelation)->indimmediate) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("ON CONFLICT does not support deferrable unique constraints/exclusion constraints as arbiters"), diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 86fa0f0deaa..c7a16b0c4a3 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -778,7 +778,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, unparented_ii = leaf_part_rri->ri_IndexRelationInfo[unparented_i]; Assert(!list_member_oid(arbiterIndexes, - unparented_rel->rd_index->indexrelid)); + RelationGetIndex(unparented_rel)->indexrelid)); /* Ignore indexes not ready */ if (!unparented_ii->ii_ReadyForInserts) @@ -798,7 +798,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, unparented_rel)) { arbiterIndexes = lappend_oid(arbiterIndexes, - unparented_rel->rd_index->indexrelid); + RelationGetIndex(unparented_rel)->indexrelid); additional_arbiters++; break; } diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index fd9efd94737..57ac8273795 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -62,7 +62,7 @@ build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, int skey_attoff = 0; Datum indclassDatum; oidvector *opclass; - int2vector *indkey = &idxrel->rd_index->indkey; + int2vector *indkey = &RelationGetIndex(idxrel)->indkey; indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, idxrel->rd_indextuple, Anum_pg_index_indclass); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index d067368cfc1..950f8b9350f 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -237,7 +237,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * Extract info from the relation descriptor for the index. */ indexRelation = index_open(indexoid, lmode); - index = indexRelation->rd_index; + index = RelationGetIndex(indexRelation); /* * Ignore invalid indexes, since they can't safely be used for @@ -921,7 +921,7 @@ infer_arbiter_indexes(PlannerInfo *root) { if (indexOidFromConstraint == RelationGetRelid(idxRel)) { - Assert(idxRel->rd_index->indisready); + Assert(RelationGetIndex(idxRel)->indisready); indexRelFromConstraint = idxRel; break; } @@ -951,7 +951,7 @@ infer_arbiter_indexes(PlannerInfo *root) * enforcement needs to occur there anyway when an inference clause is * omitted. */ - idxForm = idxRel->rd_index; + idxForm = RelationGetIndex(idxRel); /* * Ignore indexes that aren't indisready, because we cannot trust @@ -1048,7 +1048,7 @@ infer_arbiter_indexes(PlannerInfo *root) indexedAttrs = NULL; for (natt = 0; natt < idxForm->indnkeyatts; natt++) { - int attno = idxRel->rd_index->indkey.values[natt]; + int attno = RelationGetIndex(idxRel)->indkey.values[natt]; if (attno != 0) indexedAttrs = bms_add_member(indexedAttrs, @@ -1224,7 +1224,7 @@ infer_collation_opclass_match(InferenceElem *elem, Relation idxRel, Oid opfamily = idxRel->rd_opfamily[natt - 1]; Oid opcinputtype = idxRel->rd_opcintype[natt - 1]; Oid collation = idxRel->rd_indcollation[natt - 1]; - int attno = idxRel->rd_index->indkey.values[natt - 1]; + int attno = RelationGetIndex(idxRel)->indkey.values[natt - 1]; if (attno != 0) nplain++; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index f838311090b..79e9514a1f9 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -2454,7 +2454,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) /* Open the index (this will throw an error if it is not an index) */ index_rel = index_open(index_oid, AccessShareLock); - index_form = index_rel->rd_index; + index_form = RelationGetIndex(index_rel); /* Check that it does not have an associated constraint already */ if (OidIsValid(get_index_constraint(index_oid))) diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c index b71a0c9e206..bb05e0754c8 100644 --- a/src/backend/replication/logical/conflict.c +++ b/src/backend/replication/logical/conflict.c @@ -325,7 +325,7 @@ InitConflictIndexes(ResultRelInfo *relInfo) continue; /* Don't support conflict detection for deferrable index */ - if (!indexRelation->rd_index->indimmediate) + if (!RelationGetIndex(indexRelation)->indimmediate) continue; uniqueIndexes = lappend_oid(uniqueIndexes, diff --git a/src/backend/replication/logical/relation.c b/src/backend/replication/logical/relation.c index 8ffd2583afb..b9fd0233d96 100644 --- a/src/backend/replication/logical/relation.c +++ b/src/backend/replication/logical/relation.c @@ -800,7 +800,7 @@ FindUsableIndexForReplicaIdentityFull(Relation localrel, AttrMap *attrmap) * permanently invalid leftover of a failed CREATE INDEX CONCURRENTLY * must never be chosen here. */ - isUsableIdx = idxRel->rd_index->indisvalid && + isUsableIdx = RelationGetIndex(idxRel)->indisvalid && IsIndexUsableForReplicaIdentityFull(idxRel, attrmap); index_close(idxRel, AccessShareLock); @@ -849,14 +849,14 @@ IsIndexUsableForReplicaIdentityFull(Relation idxrel, AttrMap *attrmap) if (!heap_attisnull(idxrel->rd_indextuple, Anum_pg_index_indpred, NULL)) return false; - Assert(idxrel->rd_index->indnatts >= 1); + Assert(RelationGetIndex(idxrel)->indnatts >= 1); indclass = (oidvector *) DatumGetPointer(SysCacheGetAttrNotNull(INDEXRELID, idxrel->rd_indextuple, Anum_pg_index_indclass)); /* Ensure that the index has a valid equal strategy for each key column */ - for (int i = 0; i < idxrel->rd_index->indnkeyatts; i++) + for (int i = 0; i < RelationGetIndex(idxrel)->indnkeyatts; i++) { Oid opfamily; @@ -881,7 +881,7 @@ IsIndexUsableForReplicaIdentityFull(Relation idxrel, AttrMap *attrmap) } /* The leftmost index field must not be an expression */ - keycol = idxrel->rd_index->indkey.values[0]; + keycol = RelationGetIndex(idxrel)->indkey.values[0]; if (!AttributeNumberIsValid(keycol)) return false; diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c index f4ff9ab9b20..2029676463d 100644 --- a/src/backend/statistics/stat_utils.c +++ b/src/backend/statistics/stat_utils.c @@ -323,13 +323,13 @@ statatt_get_index_expr(Relation rel, int attnum) * The index's attnum points directly to a relation attnum, hence it is * not an expression attribute. */ - if (rel->rd_index->indkey.values[attnum - 1] != 0) + if (RelationGetIndex(rel)->indkey.values[attnum - 1] != 0) return NULL; indexpr_item = list_head(rel->rd_indexprs); for (int i = 0; i < attnum - 1; i++) - if (rel->rd_index->indkey.values[i] == 0) + if (RelationGetIndex(rel)->indkey.values[i] == 0) indexpr_item = lnext(rel->rd_indexprs, indexpr_item); if (indexpr_item == NULL) /* shouldn't happen */ diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index 0ae85b7d5b4..cd3f725f221 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -2558,7 +2558,7 @@ PredicateLockTID(Relation relation, const ItemPointerData *tid, Snapshot snapsho /* * Return if this xact wrote it. */ - if (relation->rd_index == NULL) + if (RelationGetIndex(relation) == NULL) { /* If we wrote it; we already have a write lock. */ if (TransactionIdIsCurrentTransactionId(tuple_xid)) @@ -2890,7 +2890,7 @@ DropAllPredicateLocksFromTable(Relation relation, bool transfer) dbId = relation->rd_locator.dbOid; relId = relation->rd_id; - if (relation->rd_index == NULL) + if (RelationGetIndex(relation) == NULL) { isIndex = false; heapId = relId; @@ -2898,7 +2898,7 @@ DropAllPredicateLocksFromTable(Relation relation, bool transfer) else { isIndex = true; - heapId = relation->rd_index->indrelid; + heapId = RelationGetIndex(relation)->indrelid; } Assert(heapId != InvalidOid); Assert(transfer || !isIndex); /* index OID only makes sense with @@ -4371,7 +4371,7 @@ CheckTableForSerializableConflictIn(Relation relation) */ MyXactDidWrite = true; - Assert(relation->rd_index == NULL); /* not an index relation */ + Assert(RelationGetIndex(relation) == NULL); /* not an index relation */ dbId = relation->rd_locator.dbOid; heapId = relation->rd_id; diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 43f40172b67..529ffbe1be0 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -3555,9 +3555,9 @@ ri_check_fastpath_index(RI_ConstraintInfo *riinfo, * collation to SPI. Map index keys to table attributes because the FK * columns need not be listed in index order. Ignore INCLUDE columns. */ - for (int i = 0; i < idx_rel->rd_index->indnkeyatts; i++) + for (int i = 0; i < RelationGetIndex(idx_rel)->indnkeyatts; i++) { - AttrNumber attnum = idx_rel->rd_index->indkey.values[i]; + AttrNumber attnum = RelationGetIndex(idx_rel)->indkey.values[i]; if (idx_rel->rd_indcollation[i] != RIAttCollation(pk_rel, attnum)) { @@ -3744,7 +3744,7 @@ ri_populate_fastpath_metadata(RI_ConstraintInfo *riinfo, */ for (idx_col = 0; idx_col < riinfo->nkeys; idx_col++) { - if (idx_rel->rd_index->indkey.values[idx_col] == riinfo->pk_attnums[i]) + if (RelationGetIndex(idx_rel)->indkey.values[idx_col] == riinfo->pk_attnums[i]) break; } Assert(idx_col < riinfo->nkeys); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 511270edfda..482c1bcdd89 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1278,8 +1278,8 @@ InitCatCachePhase2(CatCache *cache, bool touch_index) * catch thinkos in definitions of new catcaches, so we don't worry * about the pg_am indexes not getting tested. */ - Assert(idesc->rd_index->indisunique && - idesc->rd_index->indimmediate); + Assert(RelationGetIndex(idesc)->indisunique && + RelationGetIndex(idesc)->indimmediate); index_close(idesc, AccessShareLock); UnlockRelationOid(cache->cc_reloid, AccessShareLock); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 562501556c8..271e4533aa3 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1477,7 +1477,6 @@ RelationInitIndexAccessInfo(Relation relation) RelationGetRelid(relation)); oldcontext = MemoryContextSwitchTo(CacheMemoryContext); relation->rd_indextuple = heap_copytuple(tuple); - relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple); MemoryContextSwitchTo(oldcontext); ReleaseSysCache(tuple); @@ -2340,7 +2339,7 @@ RelationReloadIndexInfo(Relation relation) if (!IsSystemRelation(relation)) { HeapTuple tuple; - Form_pg_index index; + Form_pg_index index, rd_index; tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(RelationGetRelid(relation))); @@ -2355,17 +2354,18 @@ RelationReloadIndexInfo(Relation relation) * it's not worth it to track exactly which ones they are. None of * the array fields are allowed to change, though. */ - relation->rd_index->indisunique = index->indisunique; - relation->rd_index->indnullsnotdistinct = index->indnullsnotdistinct; - relation->rd_index->indisprimary = index->indisprimary; - relation->rd_index->indisexclusion = index->indisexclusion; - relation->rd_index->indimmediate = index->indimmediate; - relation->rd_index->indisclustered = index->indisclustered; - relation->rd_index->indisvalid = index->indisvalid; - relation->rd_index->indcheckxmin = index->indcheckxmin; - relation->rd_index->indisready = index->indisready; - relation->rd_index->indislive = index->indislive; - relation->rd_index->indisreplident = index->indisreplident; + rd_index = RelationGetIndex(relation); + rd_index->indisunique = index->indisunique; + rd_index->indnullsnotdistinct = index->indnullsnotdistinct; + rd_index->indisprimary = index->indisprimary; + rd_index->indisexclusion = index->indisexclusion; + rd_index->indimmediate = index->indimmediate; + rd_index->indisclustered = index->indisclustered; + rd_index->indisvalid = index->indisvalid; + rd_index->indcheckxmin = index->indcheckxmin; + rd_index->indisready = index->indisready; + rd_index->indislive = index->indislive; + rd_index->indisreplident = index->indisreplident; /* Copy xmin too, as that is needed to make sense of indcheckxmin */ HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data, @@ -5532,7 +5532,7 @@ restart: indexPredicate = NULL; /* Can this index be referenced by a foreign key? */ - isKey = indexDesc->rd_index->indisunique && + isKey = RelationGetIndex(indexDesc)->indisunique && indexExpressions == NULL && indexPredicate == NULL; @@ -5553,9 +5553,9 @@ restart: attrs = &hotblockingattrs; /* Collect simple attribute references */ - for (i = 0; i < indexDesc->rd_index->indnatts; i++) + for (i = 0; i < RelationGetIndex(indexDesc)->indnatts; i++) { - int attrnum = indexDesc->rd_index->indkey.values[i]; + int attrnum = RelationGetIndex(indexDesc)->indkey.values[i]; /* * Since we have covering indexes with non-key columns, we must @@ -5576,15 +5576,15 @@ restart: *attrs = bms_add_member(*attrs, attrnum - FirstLowInvalidHeapAttributeNumber); - if (isKey && i < indexDesc->rd_index->indnkeyatts) + if (isKey && i < RelationGetIndex(indexDesc)->indnkeyatts) uindexattrs = bms_add_member(uindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber); - if (isPK && i < indexDesc->rd_index->indnkeyatts) + if (isPK && i < RelationGetIndex(indexDesc)->indnkeyatts) pkindexattrs = bms_add_member(pkindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber); - if (isIDKey && i < indexDesc->rd_index->indnkeyatts) + if (isIDKey && i < RelationGetIndex(indexDesc)->indnkeyatts) idindexattrs = bms_add_member(idindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber); } @@ -5725,9 +5725,9 @@ RelationGetIdentityKeyBitmap(Relation relation) relation->rd_replidindex); /* Add referenced attributes to idindexattrs */ - for (i = 0; i < indexDesc->rd_index->indnatts; i++) + for (i = 0; i < RelationGetIndex(indexDesc)->indnatts; i++) { - int attrnum = indexDesc->rd_index->indkey.values[i]; + int attrnum = RelationGetIndex(indexDesc)->indkey.values[i]; /* * We don't include non-key columns into idindexattrs bitmaps. See @@ -5735,7 +5735,7 @@ RelationGetIdentityKeyBitmap(Relation relation) */ if (attrnum != 0) { - if (i < indexDesc->rd_index->indnkeyatts) + if (i < RelationGetIndex(indexDesc)->indnkeyatts) idindexattrs = bms_add_member(idindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber); } @@ -5814,7 +5814,7 @@ RelationGetExclusionInfo(Relation indexRelation, ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(indexRelation->rd_index->indrelid)); + ObjectIdGetDatum(RelationGetIndex(indexRelation)->indrelid)); conrel = table_open(ConstraintRelationId, AccessShareLock); conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true, @@ -6479,7 +6479,6 @@ load_relcache_init_file(bool shared) /* Fix up internal pointers in the tuple -- see heap_copytuple */ rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE); - rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple); /* * prepare index info context --- parameters should match diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 33a3921f5cf..598ba1cb810 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -14,6 +14,7 @@ #ifndef REL_H #define REL_H +#include "access/htup_details.h" #include "access/tupdesc.h" #include "access/xlog.h" #include "catalog/catalog.h" @@ -337,19 +338,36 @@ RelationGetLockRelId(Relation relation) */ #define RelationGetNumberOfAttributes(relation) ((relation)->rd_rel->relnatts) +/* + * RelationGetIndex + * Returns the pg_index tuple describing an index relation. + * + * rd_index is not stored directly in RelationData; it is always equal to + * GETSTRUCT(relation->rd_indextuple), so we derive it on the fly instead of + * spending a pointer-sized field on it. + */ +static inline Form_pg_index +RelationGetIndex(Relation relation) +{ + if (relation->rd_indextuple == NULL) + return NULL; + + return (Form_pg_index) GETSTRUCT(relation->rd_indextuple); +} + /* * IndexRelationGetNumberOfAttributes * Returns the number of attributes in an index. */ #define IndexRelationGetNumberOfAttributes(relation) \ - ((relation)->rd_index->indnatts) + (RelationGetIndex(relation)->indnatts) /* * IndexRelationGetNumberOfKeyAttributes * Returns the number of key attributes in an index. */ #define IndexRelationGetNumberOfKeyAttributes(relation) \ - ((relation)->rd_index->indnkeyatts) + (RelationGetIndex(relation)->indnkeyatts) /* * RelationGetDescr diff --git a/src/include/utils/rel_internal.h b/src/include/utils/rel_internal.h index 4f4b01a1894..89130218908 100644 --- a/src/include/utils/rel_internal.h +++ b/src/include/utils/rel_internal.h @@ -220,7 +220,6 @@ typedef struct RelationData /* fields used only for an index relation */ struct { - Form_pg_index rd_index; /* pg_index tuple describing this index */ /* use "struct" here to avoid needing to include htup.h: */ struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */ -- 2.55.0