LCOV - code coverage report
Current view: top level - src/backend/commands - propgraphcmds.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.2 % 685 645
Test Date: 2026-03-22 08:15:57 Functions: 100.0 % 22 22
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * propgraphcmds.c
       4              :  *    property graph manipulation
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  * src/backend/commands/propgraphcmds.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "access/genam.h"
      16              : #include "access/htup_details.h"
      17              : #include "access/nbtree.h"
      18              : #include "access/table.h"
      19              : #include "access/xact.h"
      20              : #include "catalog/catalog.h"
      21              : #include "catalog/indexing.h"
      22              : #include "catalog/namespace.h"
      23              : #include "catalog/pg_class.h"
      24              : #include "catalog/pg_collation_d.h"
      25              : #include "catalog/pg_operator_d.h"
      26              : #include "catalog/pg_propgraph_element.h"
      27              : #include "catalog/pg_propgraph_element_label.h"
      28              : #include "catalog/pg_propgraph_label.h"
      29              : #include "catalog/pg_propgraph_label_property.h"
      30              : #include "catalog/pg_propgraph_property.h"
      31              : #include "commands/defrem.h"
      32              : #include "commands/propgraphcmds.h"
      33              : #include "commands/tablecmds.h"
      34              : #include "nodes/nodeFuncs.h"
      35              : #include "parser/parse_coerce.h"
      36              : #include "parser/parse_collate.h"
      37              : #include "parser/parse_oper.h"
      38              : #include "parser/parse_relation.h"
      39              : #include "parser/parse_target.h"
      40              : #include "utils/array.h"
      41              : #include "utils/builtins.h"
      42              : #include "utils/fmgroids.h"
      43              : #include "utils/inval.h"
      44              : #include "utils/lsyscache.h"
      45              : #include "utils/rel.h"
      46              : #include "utils/ruleutils.h"
      47              : #include "utils/syscache.h"
      48              : 
      49              : 
      50              : struct element_info
      51              : {
      52              :     Oid         elementid;
      53              :     char        kind;
      54              :     Oid         relid;
      55              :     char       *aliasname;
      56              :     ArrayType  *key;
      57              : 
      58              :     char       *srcvertex;
      59              :     Oid         srcvertexid;
      60              :     Oid         srcrelid;
      61              :     ArrayType  *srckey;
      62              :     ArrayType  *srcref;
      63              :     ArrayType  *srceqop;
      64              : 
      65              :     char       *destvertex;
      66              :     Oid         destvertexid;
      67              :     Oid         destrelid;
      68              :     ArrayType  *destkey;
      69              :     ArrayType  *destref;
      70              :     ArrayType  *desteqop;
      71              : 
      72              :     List       *labels;
      73              : };
      74              : 
      75              : 
      76              : static ArrayType *propgraph_element_get_key(ParseState *pstate, const List *keycols, Relation element_rel,
      77              :                                             const char *aliasname, int location);
      78              : static void propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols,
      79              :                                         Relation edge_rel, Relation ref_rel,
      80              :                                         const char *aliasname, int location, const char *type,
      81              :                                         ArrayType **outkey, ArrayType **outref, ArrayType **outeqop);
      82              : static AttrNumber *array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel);
      83              : static ArrayType *array_from_attnums(int numattrs, const AttrNumber *attnums);
      84              : static Oid  insert_element_record(ObjectAddress pgaddress, struct element_info *einfo);
      85              : static Oid  insert_label_record(Oid graphid, Oid peoid, const char *label);
      86              : static void insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties);
      87              : static void insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr);
      88              : static void check_element_properties(Oid peoid);
      89              : static void check_element_label_properties(Oid ellabeloid);
      90              : static void check_all_labels_properties(Oid pgrelid);
      91              : static Oid  get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location);
      92              : static Oid  get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location);
      93              : static Oid  get_element_relid(Oid peid);
      94              : static List *get_graph_label_ids(Oid graphid);
      95              : static List *get_label_element_label_ids(Oid labelid);
      96              : static List *get_element_label_property_names(Oid ellabeloid);
      97              : static List *get_graph_property_ids(Oid graphid);
      98              : 
      99              : 
     100              : /*
     101              :  * CREATE PROPERTY GRAPH
     102              :  */
     103              : ObjectAddress
     104          201 : CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt)
     105              : {
     106          201 :     CreateStmt *cstmt = makeNode(CreateStmt);
     107              :     char        components_persistence;
     108              :     ListCell   *lc;
     109              :     ObjectAddress pgaddress;
     110          201 :     List       *vertex_infos = NIL;
     111          201 :     List       *edge_infos = NIL;
     112          201 :     List       *element_aliases = NIL;
     113          201 :     List       *element_oids = NIL;
     114              : 
     115          201 :     if (stmt->pgname->relpersistence == RELPERSISTENCE_UNLOGGED)
     116            4 :         ereport(ERROR,
     117              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     118              :                  errmsg("property graphs cannot be unlogged because they do not have storage")));
     119              : 
     120          197 :     components_persistence = RELPERSISTENCE_PERMANENT;
     121              : 
     122          539 :     foreach(lc, stmt->vertex_tables)
     123              :     {
     124          350 :         PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
     125              :         struct element_info *vinfo;
     126              :         Relation    rel;
     127              : 
     128          350 :         vinfo = palloc0_object(struct element_info);
     129          350 :         vinfo->kind = PGEKIND_VERTEX;
     130              : 
     131          350 :         vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
     132              : 
     133          346 :         rel = table_open(vinfo->relid, NoLock);
     134              : 
     135          346 :         if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
     136            8 :             components_persistence = RELPERSISTENCE_TEMP;
     137              : 
     138          346 :         if (vertex->vtable->alias)
     139            8 :             vinfo->aliasname = vertex->vtable->alias->aliasname;
     140              :         else
     141          338 :             vinfo->aliasname = vertex->vtable->relname;
     142              : 
     143          346 :         if (list_member(element_aliases, makeString(vinfo->aliasname)))
     144            4 :             ereport(ERROR,
     145              :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     146              :                      errmsg("alias \"%s\" used more than once as element table", vinfo->aliasname),
     147              :                      parser_errposition(pstate, vertex->location)));
     148              : 
     149          342 :         vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location);
     150              : 
     151          342 :         vinfo->labels = vertex->labels;
     152              : 
     153          342 :         table_close(rel, NoLock);
     154              : 
     155          342 :         vertex_infos = lappend(vertex_infos, vinfo);
     156              : 
     157          342 :         element_aliases = lappend(element_aliases, makeString(vinfo->aliasname));
     158              :     }
     159              : 
     160          346 :     foreach(lc, stmt->edge_tables)
     161              :     {
     162          177 :         PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc);
     163              :         struct element_info *einfo;
     164              :         Relation    rel;
     165              :         ListCell   *lc2;
     166              :         Oid         srcrelid;
     167              :         Oid         destrelid;
     168              :         Relation    srcrel;
     169              :         Relation    destrel;
     170              : 
     171          177 :         einfo = palloc0_object(struct element_info);
     172          177 :         einfo->kind = PGEKIND_EDGE;
     173              : 
     174          177 :         einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
     175              : 
     176          177 :         rel = table_open(einfo->relid, NoLock);
     177              : 
     178          177 :         if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
     179            0 :             components_persistence = RELPERSISTENCE_TEMP;
     180              : 
     181          177 :         if (edge->etable->alias)
     182            0 :             einfo->aliasname = edge->etable->alias->aliasname;
     183              :         else
     184          177 :             einfo->aliasname = edge->etable->relname;
     185              : 
     186          177 :         if (list_member(element_aliases, makeString(einfo->aliasname)))
     187            0 :             ereport(ERROR,
     188              :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     189              :                      errmsg("alias \"%s\" used more than once as element table", einfo->aliasname),
     190              :                      parser_errposition(pstate, edge->location)));
     191              : 
     192          177 :         einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location);
     193              : 
     194          177 :         einfo->srcvertex = edge->esrcvertex;
     195          177 :         einfo->destvertex = edge->edestvertex;
     196              : 
     197          177 :         srcrelid = 0;
     198          177 :         destrelid = 0;
     199          423 :         foreach(lc2, vertex_infos)
     200              :         {
     201          415 :             struct element_info *vinfo = lfirst(lc2);
     202              : 
     203          415 :             if (strcmp(vinfo->aliasname, edge->esrcvertex) == 0)
     204          173 :                 srcrelid = vinfo->relid;
     205              : 
     206          415 :             if (strcmp(vinfo->aliasname, edge->edestvertex) == 0)
     207          173 :                 destrelid = vinfo->relid;
     208              : 
     209          415 :             if (srcrelid && destrelid)
     210          169 :                 break;
     211              :         }
     212          177 :         if (!srcrelid)
     213            4 :             ereport(ERROR,
     214              :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     215              :                      errmsg("source vertex \"%s\" of edge \"%s\" does not exist",
     216              :                             edge->esrcvertex, einfo->aliasname),
     217              :                      parser_errposition(pstate, edge->location)));
     218          173 :         if (!destrelid)
     219            4 :             ereport(ERROR,
     220              :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     221              :                      errmsg("destination vertex \"%s\" of edge \"%s\" does not exist",
     222              :                             edge->edestvertex, einfo->aliasname),
     223              :                      parser_errposition(pstate, edge->location)));
     224              : 
     225          169 :         srcrel = table_open(srcrelid, NoLock);
     226          169 :         destrel = table_open(destrelid, NoLock);
     227              : 
     228          169 :         propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel,
     229          169 :                                     einfo->aliasname, edge->location, "SOURCE",
     230              :                                     &einfo->srckey, &einfo->srcref, &einfo->srceqop);
     231          161 :         propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel,
     232          161 :                                     einfo->aliasname, edge->location, "DESTINATION",
     233              :                                     &einfo->destkey, &einfo->destref, &einfo->desteqop);
     234              : 
     235          157 :         einfo->labels = edge->labels;
     236              : 
     237          157 :         table_close(destrel, NoLock);
     238          157 :         table_close(srcrel, NoLock);
     239              : 
     240          157 :         table_close(rel, NoLock);
     241              : 
     242          157 :         edge_infos = lappend(edge_infos, einfo);
     243              : 
     244          157 :         element_aliases = lappend(element_aliases, makeString(einfo->aliasname));
     245              :     }
     246              : 
     247          169 :     cstmt->relation = stmt->pgname;
     248          169 :     cstmt->oncommit = ONCOMMIT_NOOP;
     249              : 
     250              :     /*
     251              :      * Automatically make it temporary if any component tables are temporary
     252              :      * (see also DefineView()).
     253              :      */
     254          169 :     if (stmt->pgname->relpersistence == RELPERSISTENCE_PERMANENT
     255          157 :         && components_persistence == RELPERSISTENCE_TEMP)
     256              :     {
     257            4 :         cstmt->relation = copyObject(cstmt->relation);
     258            4 :         cstmt->relation->relpersistence = RELPERSISTENCE_TEMP;
     259            4 :         ereport(NOTICE,
     260              :                 (errmsg("property graph \"%s\" will be temporary",
     261              :                         stmt->pgname->relname)));
     262              :     }
     263              : 
     264          169 :     pgaddress = DefineRelation(cstmt, RELKIND_PROPGRAPH, InvalidOid, NULL, NULL);
     265              : 
     266          443 :     foreach(lc, vertex_infos)
     267              :     {
     268          294 :         struct element_info *vinfo = lfirst(lc);
     269              :         Oid         peoid;
     270              : 
     271          294 :         peoid = insert_element_record(pgaddress, vinfo);
     272          278 :         element_oids = lappend_oid(element_oids, peoid);
     273              :     }
     274              : 
     275          302 :     foreach(lc, edge_infos)
     276              :     {
     277          157 :         struct element_info *einfo = lfirst(lc);
     278              :         Oid         peoid;
     279              :         ListCell   *lc2;
     280              : 
     281              :         /*
     282              :          * Look up the vertices again.  Now the vertices have OIDs assigned,
     283              :          * which we need.
     284              :          */
     285          375 :         foreach(lc2, vertex_infos)
     286              :         {
     287          375 :             struct element_info *vinfo = lfirst(lc2);
     288              : 
     289          375 :             if (strcmp(vinfo->aliasname, einfo->srcvertex) == 0)
     290              :             {
     291          157 :                 einfo->srcvertexid = vinfo->elementid;
     292          157 :                 einfo->srcrelid = vinfo->relid;
     293              :             }
     294          375 :             if (strcmp(vinfo->aliasname, einfo->destvertex) == 0)
     295              :             {
     296          157 :                 einfo->destvertexid = vinfo->elementid;
     297          157 :                 einfo->destrelid = vinfo->relid;
     298              :             }
     299          375 :             if (einfo->srcvertexid && einfo->destvertexid)
     300          157 :                 break;
     301              :         }
     302              :         Assert(einfo->srcvertexid);
     303              :         Assert(einfo->destvertexid);
     304              :         Assert(einfo->srcrelid);
     305              :         Assert(einfo->destrelid);
     306          157 :         peoid = insert_element_record(pgaddress, einfo);
     307          153 :         element_oids = lappend_oid(element_oids, peoid);
     308              :     }
     309              : 
     310          145 :     CommandCounterIncrement();
     311              : 
     312          681 :     foreach_oid(peoid, element_oids)
     313          399 :         check_element_properties(peoid);
     314          141 :     check_all_labels_properties(pgaddress.objectId);
     315              : 
     316          129 :     return pgaddress;
     317              : }
     318              : 
     319              : /*
     320              :  * Process the key clause specified for an element.  If key_clause is non-NIL,
     321              :  * then it is a list of column names.  Otherwise, the primary key of the
     322              :  * relation is used.  The return value is an array of column numbers.
     323              :  */
     324              : static ArrayType *
     325          583 : propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, const char *aliasname, int location)
     326              : {
     327              :     ArrayType  *a;
     328              : 
     329          583 :     if (key_clause == NIL)
     330              :     {
     331          116 :         Oid         pkidx = RelationGetPrimaryKeyIndex(element_rel, false);
     332              : 
     333          116 :         if (!pkidx)
     334            0 :             ereport(ERROR,
     335              :                     errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     336              :                     errmsg("no key specified and no suitable primary key exists for definition of element \"%s\"", aliasname),
     337              :                     parser_errposition(pstate, location));
     338              :         else
     339              :         {
     340              :             Relation    indexDesc;
     341              : 
     342          116 :             indexDesc = index_open(pkidx, AccessShareLock);
     343          116 :             a = array_from_attnums(indexDesc->rd_index->indkey.dim1, indexDesc->rd_index->indkey.values);
     344          116 :             index_close(indexDesc, NoLock);
     345              :         }
     346              :     }
     347              :     else
     348              :     {
     349          467 :         a = array_from_attnums(list_length(key_clause),
     350          467 :                                array_from_column_list(pstate, key_clause, location, element_rel));
     351              :     }
     352              : 
     353          583 :     return a;
     354              : }
     355              : 
     356              : /*
     357              :  * Process the source or destination link of an edge.
     358              :  *
     359              :  * keycols and refcols are column names representing the local and referenced
     360              :  * (vertex) columns.  If they are both NIL, a matching foreign key is looked
     361              :  * up.
     362              :  *
     363              :  * edge_rel and ref_rel are the local and referenced element tables.
     364              :  *
     365              :  * aliasname, location, and type are for error messages.  type is either
     366              :  * "SOURCE" or "DESTINATION".
     367              :  *
     368              :  * The outputs are arrays of column numbers in outkey and outref.
     369              :  */
     370              : static void
     371          402 : propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols,
     372              :                             Relation edge_rel, Relation ref_rel,
     373              :                             const char *aliasname, int location, const char *type,
     374              :                             ArrayType **outkey, ArrayType **outref, ArrayType **outeqop)
     375              : {
     376              :     int         nkeys;
     377              :     AttrNumber *keyattnums;
     378              :     AttrNumber *refattnums;
     379              :     Oid        *keyeqops;
     380              :     Datum      *datums;
     381              : 
     382              :     Assert((keycols && refcols) || (!keycols && !refcols));
     383              : 
     384          402 :     if (keycols)
     385              :     {
     386          390 :         if (list_length(keycols) != list_length(refcols))
     387            0 :             ereport(ERROR,
     388              :                     errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     389              :                     errmsg("mismatching number of columns in %s vertex definition of edge \"%s\"", type, aliasname),
     390              :                     parser_errposition(pstate, location));
     391              : 
     392          390 :         nkeys = list_length(keycols);
     393          390 :         keyattnums = array_from_column_list(pstate, keycols, location, edge_rel);
     394          390 :         refattnums = array_from_column_list(pstate, refcols, location, ref_rel);
     395          390 :         keyeqops = palloc_array(Oid, nkeys);
     396              : 
     397          820 :         for (int i = 0; i < nkeys; i++)
     398              :         {
     399              :             Oid         keytype;
     400              :             int32       keytypmod;
     401              :             Oid         keycoll;
     402              :             Oid         reftype;
     403              :             int32       reftypmod;
     404              :             Oid         refcoll;
     405              :             Oid         opc;
     406              :             Oid         opf;
     407              :             StrategyNumber strategy;
     408              : 
     409              :             /*
     410              :              * Lookup equality operator to be used for edge and vertex key.
     411              :              * Vertex key is equivalent to primary key and edge key is similar
     412              :              * to foreign key since edge key references vertex key. Hence
     413              :              * vertex key is used as left operand and edge key is used as
     414              :              * right operand. The method used to find the equality operators
     415              :              * is similar to the method used to find equality operators for
     416              :              * FK/PK comparison in ATAddForeignKeyConstraint() except that
     417              :              * opclass of the the vertex key type is used as a starting point.
     418              :              * Since we need only equality operators we use both BT and HASH
     419              :              * strategies.
     420              :              *
     421              :              * If the required operators do not exist, we can not construct
     422              :              * quals linking an edge to its adjacent vertexes.
     423              :              */
     424          438 :             get_atttypetypmodcoll(RelationGetRelid(edge_rel), keyattnums[i], &keytype, &keytypmod, &keycoll);
     425          438 :             get_atttypetypmodcoll(RelationGetRelid(ref_rel), refattnums[i], &reftype, &reftypmod, &refcoll);
     426          438 :             keyeqops[i] = InvalidOid;
     427          438 :             strategy = BTEqualStrategyNumber;
     428          438 :             opc = GetDefaultOpClass(reftype, BTREE_AM_OID);
     429          438 :             if (!OidIsValid(opc))
     430              :             {
     431            0 :                 opc = GetDefaultOpClass(reftype, HASH_AM_OID);
     432            0 :                 strategy = HTEqualStrategyNumber;
     433              :             }
     434          438 :             if (OidIsValid(opc))
     435              :             {
     436          438 :                 opf = get_opclass_family(opc);
     437          438 :                 if (OidIsValid(opf))
     438              :                 {
     439          438 :                     keyeqops[i] = get_opfamily_member(opf, reftype, keytype, strategy);
     440          438 :                     if (!OidIsValid(keyeqops[i]))
     441              :                     {
     442              :                         /* Last resort, implicit cast. */
     443            4 :                         if (can_coerce_type(1, &keytype, &reftype, COERCION_IMPLICIT))
     444            0 :                             keyeqops[i] = get_opfamily_member(opf, reftype, reftype, strategy);
     445              :                     }
     446              :                 }
     447              :             }
     448              : 
     449          438 :             if (!OidIsValid(keyeqops[i]))
     450            4 :                 ereport(ERROR,
     451              :                         errcode(ERRCODE_SYNTAX_ERROR),
     452              :                         errmsg("no equality operator exists for %s key comparison of edge \"%s\"",
     453              :                                type, aliasname),
     454              :                         parser_errposition(pstate, location));
     455              : 
     456              :             /*
     457              :              * If collations of key attribute and referenced attribute are
     458              :              * different, an edge may end up being adjacent to undesired
     459              :              * vertexes.  Prohibit such a case.
     460              :              *
     461              :              * PK/FK allows different collations as long as they are
     462              :              * deterministic for backward compatibility. But we can be a bit
     463              :              * stricter here and follow SQL standard.
     464              :              */
     465          434 :             if (keycoll != refcoll &&
     466            4 :                 keycoll != DEFAULT_COLLATION_OID && refcoll != DEFAULT_COLLATION_OID &&
     467            4 :                 OidIsValid(keycoll) && OidIsValid(refcoll))
     468            4 :                 ereport(ERROR,
     469              :                         errcode(ERRCODE_SYNTAX_ERROR),
     470              :                         errmsg("collation mismatch in %s key of edge \"%s\": %s vs. %s",
     471              :                                type, aliasname,
     472              :                                get_collation_name(keycoll), get_collation_name(refcoll)),
     473              :                         parser_errposition(pstate, location));
     474              :         }
     475              :     }
     476              :     else
     477              :     {
     478           12 :         ForeignKeyCacheInfo *fk = NULL;
     479              : 
     480           40 :         foreach_node(ForeignKeyCacheInfo, tmp, RelationGetFKeyList(edge_rel))
     481              :         {
     482           16 :             if (tmp->confrelid == RelationGetRelid(ref_rel))
     483              :             {
     484            8 :                 if (fk)
     485            0 :                     ereport(ERROR,
     486              :                             errcode(ERRCODE_SYNTAX_ERROR),
     487              :                             errmsg("more than one suitable foreign key exists for %s key of edge \"%s\"", type, aliasname),
     488              :                             parser_errposition(pstate, location));
     489            8 :                 fk = tmp;
     490              :             }
     491              :         }
     492              : 
     493           12 :         if (!fk)
     494            4 :             ereport(ERROR,
     495              :                     errcode(ERRCODE_SYNTAX_ERROR),
     496              :                     errmsg("no %s key specified and no suitable foreign key exists for definition of edge \"%s\"", type, aliasname),
     497              :                     parser_errposition(pstate, location));
     498              : 
     499            8 :         nkeys = fk->nkeys;
     500            8 :         keyattnums = fk->conkey;
     501            8 :         refattnums = fk->confkey;
     502            8 :         keyeqops = fk->conpfeqop;
     503              :     }
     504              : 
     505          390 :     *outkey = array_from_attnums(nkeys, keyattnums);
     506          390 :     *outref = array_from_attnums(nkeys, refattnums);
     507          390 :     datums = palloc_array(Datum, nkeys);
     508          828 :     for (int i = 0; i < nkeys; i++)
     509          438 :         datums[i] = ObjectIdGetDatum(keyeqops[i]);
     510          390 :     *outeqop = construct_array_builtin(datums, nkeys, OIDOID);
     511          390 : }
     512              : 
     513              : /*
     514              :  * Convert list of column names in the specified relation into an array of
     515              :  * column numbers.
     516              :  */
     517              : static AttrNumber *
     518         1247 : array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel)
     519              : {
     520              :     int         numattrs;
     521              :     AttrNumber *attnums;
     522              :     int         i;
     523              :     ListCell   *lc;
     524              : 
     525         1247 :     numattrs = list_length(colnames);
     526         1247 :     attnums = palloc_array(AttrNumber, numattrs);
     527              : 
     528         1247 :     i = 0;
     529         2751 :     foreach(lc, colnames)
     530              :     {
     531         1504 :         char       *colname = strVal(lfirst(lc));
     532         1504 :         Oid         relid = RelationGetRelid(element_rel);
     533              :         AttrNumber  attnum;
     534              : 
     535         1504 :         attnum = get_attnum(relid, colname);
     536         1504 :         if (!attnum)
     537            0 :             ereport(ERROR,
     538              :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
     539              :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
     540              :                             colname, get_rel_name(relid)),
     541              :                      parser_errposition(pstate, location)));
     542         1504 :         attnums[i++] = attnum;
     543              :     }
     544              : 
     545         2751 :     for (int j = 0; j < numattrs; j++)
     546              :     {
     547         1795 :         for (int k = j + 1; k < numattrs; k++)
     548              :         {
     549          291 :             if (attnums[j] == attnums[k])
     550            0 :                 ereport(ERROR,
     551              :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     552              :                          errmsg("graph key columns list must not contain duplicates"),
     553              :                          parser_errposition(pstate, location)));
     554              :         }
     555              :     }
     556              : 
     557         1247 :     return attnums;
     558              : }
     559              : 
     560              : static ArrayType *
     561         1363 : array_from_attnums(int numattrs, const AttrNumber *attnums)
     562              : {
     563              :     Datum      *attnumsd;
     564              : 
     565         1363 :     attnumsd = palloc_array(Datum, numattrs);
     566              : 
     567         3015 :     for (int i = 0; i < numattrs; i++)
     568         1652 :         attnumsd[i] = Int16GetDatum(attnums[i]);
     569              : 
     570         1363 :     return construct_array_builtin(attnumsd, numattrs, INT2OID);
     571              : }
     572              : 
     573              : static void
     574         1283 : array_of_attnums_to_objectaddrs(Oid relid, ArrayType *arr, ObjectAddresses *addrs)
     575              : {
     576              :     Datum      *attnumsd;
     577              :     int         numattrs;
     578              : 
     579         1283 :     deconstruct_array_builtin(arr, INT2OID, &attnumsd, NULL, &numattrs);
     580              : 
     581         2835 :     for (int i = 0; i < numattrs; i++)
     582              :     {
     583              :         ObjectAddress referenced;
     584              : 
     585         1552 :         ObjectAddressSubSet(referenced, RelationRelationId, relid, DatumGetInt16(attnumsd[i]));
     586         1552 :         add_exact_object_address(&referenced, addrs);
     587              :     }
     588         1283 : }
     589              : 
     590              : static void
     591          386 : array_of_opers_to_objectaddrs(ArrayType *arr, ObjectAddresses *addrs)
     592              : {
     593              :     Datum      *opersd;
     594              :     int         numopers;
     595              : 
     596          386 :     deconstruct_array_builtin(arr, OIDOID, &opersd, NULL, &numopers);
     597              : 
     598          820 :     for (int i = 0; i < numopers; i++)
     599              :     {
     600              :         ObjectAddress referenced;
     601              : 
     602          434 :         ObjectAddressSet(referenced, OperatorRelationId, DatumGetObjectId(opersd[i]));
     603          434 :         add_exact_object_address(&referenced, addrs);
     604              :     }
     605          386 : }
     606              : 
     607              : /*
     608              :  * Insert a record for an element into the pg_propgraph_element catalog.  Also
     609              :  * inserts labels and properties into their respective catalogs.
     610              :  */
     611              : static Oid
     612          511 : insert_element_record(ObjectAddress pgaddress, struct element_info *einfo)
     613              : {
     614          511 :     Oid         graphid = pgaddress.objectId;
     615              :     Relation    rel;
     616              :     NameData    aliasname;
     617              :     Oid         peoid;
     618          511 :     Datum       values[Natts_pg_propgraph_element] = {0};
     619          511 :     bool        nulls[Natts_pg_propgraph_element] = {0};
     620              :     HeapTuple   tup;
     621              :     ObjectAddress myself;
     622              :     ObjectAddress referenced;
     623              :     ObjectAddresses *addrs;
     624              : 
     625          511 :     rel = table_open(PropgraphElementRelationId, RowExclusiveLock);
     626              : 
     627          511 :     peoid = GetNewOidWithIndex(rel, PropgraphElementObjectIndexId, Anum_pg_propgraph_element_oid);
     628          511 :     einfo->elementid = peoid;
     629          511 :     values[Anum_pg_propgraph_element_oid - 1] = ObjectIdGetDatum(peoid);
     630          511 :     values[Anum_pg_propgraph_element_pgepgid - 1] = ObjectIdGetDatum(graphid);
     631          511 :     values[Anum_pg_propgraph_element_pgerelid - 1] = ObjectIdGetDatum(einfo->relid);
     632          511 :     namestrcpy(&aliasname, einfo->aliasname);
     633          511 :     values[Anum_pg_propgraph_element_pgealias - 1] = NameGetDatum(&aliasname);
     634          511 :     values[Anum_pg_propgraph_element_pgekind - 1] = CharGetDatum(einfo->kind);
     635          511 :     values[Anum_pg_propgraph_element_pgesrcvertexid - 1] = ObjectIdGetDatum(einfo->srcvertexid);
     636          511 :     values[Anum_pg_propgraph_element_pgedestvertexid - 1] = ObjectIdGetDatum(einfo->destvertexid);
     637          511 :     values[Anum_pg_propgraph_element_pgekey - 1] = PointerGetDatum(einfo->key);
     638              : 
     639          511 :     if (einfo->srckey)
     640          193 :         values[Anum_pg_propgraph_element_pgesrckey - 1] = PointerGetDatum(einfo->srckey);
     641              :     else
     642          318 :         nulls[Anum_pg_propgraph_element_pgesrckey - 1] = true;
     643          511 :     if (einfo->srcref)
     644          193 :         values[Anum_pg_propgraph_element_pgesrcref - 1] = PointerGetDatum(einfo->srcref);
     645              :     else
     646          318 :         nulls[Anum_pg_propgraph_element_pgesrcref - 1] = true;
     647          511 :     if (einfo->srceqop)
     648          193 :         values[Anum_pg_propgraph_element_pgesrceqop - 1] = PointerGetDatum(einfo->srceqop);
     649              :     else
     650          318 :         nulls[Anum_pg_propgraph_element_pgesrceqop - 1] = true;
     651          511 :     if (einfo->destkey)
     652          193 :         values[Anum_pg_propgraph_element_pgedestkey - 1] = PointerGetDatum(einfo->destkey);
     653              :     else
     654          318 :         nulls[Anum_pg_propgraph_element_pgedestkey - 1] = true;
     655          511 :     if (einfo->destref)
     656          193 :         values[Anum_pg_propgraph_element_pgedestref - 1] = PointerGetDatum(einfo->destref);
     657              :     else
     658          318 :         nulls[Anum_pg_propgraph_element_pgedestref - 1] = true;
     659          511 :     if (einfo->desteqop)
     660          193 :         values[Anum_pg_propgraph_element_pgedesteqop - 1] = PointerGetDatum(einfo->desteqop);
     661              :     else
     662          318 :         nulls[Anum_pg_propgraph_element_pgedesteqop - 1] = true;
     663              : 
     664          511 :     tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
     665          511 :     CatalogTupleInsert(rel, tup);
     666          511 :     heap_freetuple(tup);
     667              : 
     668          511 :     ObjectAddressSet(myself, PropgraphElementRelationId, peoid);
     669              : 
     670              :     /* Add dependency on the property graph */
     671          511 :     recordDependencyOn(&myself, &pgaddress, DEPENDENCY_AUTO);
     672              : 
     673          511 :     addrs = new_object_addresses();
     674              : 
     675              :     /* Add dependency on the relation */
     676          511 :     ObjectAddressSet(referenced, RelationRelationId, einfo->relid);
     677          511 :     add_exact_object_address(&referenced, addrs);
     678          511 :     array_of_attnums_to_objectaddrs(einfo->relid, einfo->key, addrs);
     679              : 
     680              :     /*
     681              :      * Add dependencies on vertices and equality operators used for key
     682              :      * comparison.
     683              :      */
     684          511 :     if (einfo->srcvertexid)
     685              :     {
     686          193 :         ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->srcvertexid);
     687          193 :         add_exact_object_address(&referenced, addrs);
     688          193 :         array_of_attnums_to_objectaddrs(einfo->relid, einfo->srckey, addrs);
     689          193 :         array_of_attnums_to_objectaddrs(einfo->srcrelid, einfo->srcref, addrs);
     690          193 :         array_of_opers_to_objectaddrs(einfo->srceqop, addrs);
     691              :     }
     692          511 :     if (einfo->destvertexid)
     693              :     {
     694          193 :         ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->destvertexid);
     695          193 :         add_exact_object_address(&referenced, addrs);
     696          193 :         array_of_attnums_to_objectaddrs(einfo->relid, einfo->destkey, addrs);
     697          193 :         array_of_attnums_to_objectaddrs(einfo->destrelid, einfo->destref, addrs);
     698          193 :         array_of_opers_to_objectaddrs(einfo->desteqop, addrs);
     699              :     }
     700              : 
     701          511 :     record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
     702              : 
     703          511 :     table_close(rel, NoLock);
     704              : 
     705          511 :     if (einfo->labels)
     706              :     {
     707              :         ListCell   *lc;
     708              : 
     709         1110 :         foreach(lc, einfo->labels)
     710              :         {
     711          627 :             PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
     712              :             Oid         ellabeloid;
     713              : 
     714          627 :             if (lp->label)
     715          256 :                 ellabeloid = insert_label_record(graphid, peoid, lp->label);
     716              :             else
     717          371 :                 ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname);
     718          627 :             insert_property_records(graphid, ellabeloid, einfo->relid, lp->properties);
     719              : 
     720          599 :             CommandCounterIncrement();
     721              :         }
     722              :     }
     723              :     else
     724              :     {
     725              :         Oid         ellabeloid;
     726            0 :         PropGraphProperties *pr = makeNode(PropGraphProperties);
     727              : 
     728            0 :         pr->all = true;
     729            0 :         pr->location = -1;
     730              : 
     731            0 :         ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname);
     732            0 :         insert_property_records(graphid, ellabeloid, einfo->relid, pr);
     733              :     }
     734              : 
     735          483 :     return peoid;
     736              : }
     737              : 
     738              : /*
     739              :  * Insert records for a label into the pg_propgraph_label and
     740              :  * pg_propgraph_element_label catalogs, and register dependencies.
     741              :  *
     742              :  * Returns the OID of the new pg_propgraph_element_label record.
     743              :  */
     744              : static Oid
     745          655 : insert_label_record(Oid graphid, Oid peoid, const char *label)
     746              : {
     747              :     Oid         labeloid;
     748              :     Oid         ellabeloid;
     749              : 
     750              :     /*
     751              :      * Insert into pg_propgraph_label if not already existing.
     752              :      */
     753          655 :     labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(graphid), CStringGetDatum(label));
     754          655 :     if (!labeloid)
     755              :     {
     756              :         Relation    rel;
     757          506 :         Datum       values[Natts_pg_propgraph_label] = {0};
     758          506 :         bool        nulls[Natts_pg_propgraph_label] = {0};
     759              :         NameData    labelname;
     760              :         HeapTuple   tup;
     761              :         ObjectAddress myself;
     762              :         ObjectAddress referenced;
     763              : 
     764          506 :         rel = table_open(PropgraphLabelRelationId, RowExclusiveLock);
     765              : 
     766          506 :         labeloid = GetNewOidWithIndex(rel, PropgraphLabelObjectIndexId, Anum_pg_propgraph_label_oid);
     767          506 :         values[Anum_pg_propgraph_label_oid - 1] = ObjectIdGetDatum(labeloid);
     768          506 :         values[Anum_pg_propgraph_label_pglpgid - 1] = ObjectIdGetDatum(graphid);
     769          506 :         namestrcpy(&labelname, label);
     770          506 :         values[Anum_pg_propgraph_label_pgllabel - 1] = NameGetDatum(&labelname);
     771              : 
     772          506 :         tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
     773          506 :         CatalogTupleInsert(rel, tup);
     774          506 :         heap_freetuple(tup);
     775              : 
     776          506 :         ObjectAddressSet(myself, PropgraphLabelRelationId, labeloid);
     777              : 
     778          506 :         ObjectAddressSet(referenced, RelationRelationId, graphid);
     779          506 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     780              : 
     781          506 :         table_close(rel, NoLock);
     782              :     }
     783              : 
     784              :     /*
     785              :      * Insert into pg_propgraph_element_label
     786              :      */
     787              :     {
     788              :         Relation    rel;
     789          655 :         Datum       values[Natts_pg_propgraph_element_label] = {0};
     790          655 :         bool        nulls[Natts_pg_propgraph_element_label] = {0};
     791              :         HeapTuple   tup;
     792              :         ObjectAddress myself;
     793              :         ObjectAddress referenced;
     794              : 
     795          655 :         rel = table_open(PropgraphElementLabelRelationId, RowExclusiveLock);
     796              : 
     797          655 :         ellabeloid = GetNewOidWithIndex(rel, PropgraphElementLabelObjectIndexId, Anum_pg_propgraph_element_label_oid);
     798          655 :         values[Anum_pg_propgraph_element_label_oid - 1] = ObjectIdGetDatum(ellabeloid);
     799          655 :         values[Anum_pg_propgraph_element_label_pgellabelid - 1] = ObjectIdGetDatum(labeloid);
     800          655 :         values[Anum_pg_propgraph_element_label_pgelelid - 1] = ObjectIdGetDatum(peoid);
     801              : 
     802          655 :         tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
     803          655 :         CatalogTupleInsert(rel, tup);
     804          655 :         heap_freetuple(tup);
     805              : 
     806          655 :         ObjectAddressSet(myself, PropgraphElementLabelRelationId, ellabeloid);
     807              : 
     808          655 :         ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid);
     809          655 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     810          655 :         ObjectAddressSet(referenced, PropgraphElementRelationId, peoid);
     811          655 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     812              : 
     813          655 :         table_close(rel, NoLock);
     814              :     }
     815              : 
     816          655 :     return ellabeloid;
     817              : }
     818              : 
     819              : /*
     820              :  * Insert records for properties into the pg_propgraph_property catalog.
     821              :  */
     822              : static void
     823          663 : insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties)
     824              : {
     825          663 :     List       *proplist = NIL;
     826              :     ParseState *pstate;
     827              :     ParseNamespaceItem *nsitem;
     828              :     List       *tp;
     829              :     Relation    rel;
     830              :     ListCell   *lc;
     831              : 
     832          663 :     if (properties->all)
     833              :     {
     834              :         Relation    attRelation;
     835              :         SysScanDesc scan;
     836              :         ScanKeyData key[1];
     837              :         HeapTuple   attributeTuple;
     838              : 
     839          286 :         attRelation = table_open(AttributeRelationId, RowShareLock);
     840          286 :         ScanKeyInit(&key[0],
     841              :                     Anum_pg_attribute_attrelid,
     842              :                     BTEqualStrategyNumber, F_OIDEQ,
     843              :                     ObjectIdGetDatum(pgerelid));
     844          286 :         scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
     845              :                                   true, NULL, 1, key);
     846         2758 :         while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
     847              :         {
     848         2472 :             Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
     849              :             ColumnRef  *cr;
     850              :             ResTarget  *rt;
     851              : 
     852         2472 :             if (att->attnum <= 0 || att->attisdropped)
     853         1704 :                 continue;
     854              : 
     855          768 :             cr = makeNode(ColumnRef);
     856          768 :             rt = makeNode(ResTarget);
     857              : 
     858          768 :             cr->fields = list_make1(makeString(pstrdup(NameStr(att->attname))));
     859          768 :             cr->location = -1;
     860              : 
     861          768 :             rt->name = pstrdup(NameStr(att->attname));
     862          768 :             rt->val = (Node *) cr;
     863          768 :             rt->location = -1;
     864              : 
     865          768 :             proplist = lappend(proplist, rt);
     866              :         }
     867          286 :         systable_endscan(scan);
     868          286 :         table_close(attRelation, RowShareLock);
     869              :     }
     870              :     else
     871              :     {
     872          377 :         proplist = properties->properties;
     873              : 
     874         1011 :         foreach(lc, proplist)
     875              :         {
     876          634 :             ResTarget  *rt = lfirst_node(ResTarget, lc);
     877              : 
     878          634 :             if (!rt->name && !IsA(rt->val, ColumnRef))
     879            0 :                 ereport(ERROR,
     880              :                         errcode(ERRCODE_SYNTAX_ERROR),
     881              :                         errmsg("property name required"),
     882              :                         parser_errposition(NULL, rt->location));
     883              :         }
     884              :     }
     885              : 
     886          663 :     rel = table_open(pgerelid, AccessShareLock);
     887              : 
     888          663 :     pstate = make_parsestate(NULL);
     889          663 :     nsitem = addRangeTableEntryForRelation(pstate,
     890              :                                            rel,
     891              :                                            AccessShareLock,
     892              :                                            NULL,
     893              :                                            false,
     894              :                                            true);
     895          663 :     addNSItemToQuery(pstate, nsitem, true, true, true);
     896              : 
     897          663 :     table_close(rel, NoLock);
     898              : 
     899          663 :     tp = transformTargetList(pstate, proplist, EXPR_KIND_PROPGRAPH_PROPERTY);
     900          663 :     assign_expr_collations(pstate, (Node *) tp);
     901              : 
     902         2033 :     foreach(lc, tp)
     903              :     {
     904         1402 :         TargetEntry *te = lfirst_node(TargetEntry, lc);
     905              : 
     906         1402 :         insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr);
     907              :     }
     908          631 : }
     909              : 
     910              : /*
     911              :  * Insert records for a property into the pg_propgraph_property and
     912              :  * pg_propgraph_label_property catalogs, and register dependencies.
     913              :  */
     914              : static void
     915         1402 : insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr)
     916              : {
     917              :     Oid         propoid;
     918         1402 :     Oid         exprtypid = exprType((const Node *) expr);
     919         1402 :     int32       exprtypmod = exprTypmod((const Node *) expr);
     920         1402 :     Oid         exprcollation = exprCollation((const Node *) expr);
     921              : 
     922              :     /*
     923              :      * Insert into pg_propgraph_property if not already existing.
     924              :      */
     925         1402 :     propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, Anum_pg_propgraph_property_oid, ObjectIdGetDatum(graphid), CStringGetDatum(propname));
     926         1402 :     if (!OidIsValid(propoid))
     927              :     {
     928              :         Relation    rel;
     929              :         NameData    propnamedata;
     930          759 :         Datum       values[Natts_pg_propgraph_property] = {0};
     931          759 :         bool        nulls[Natts_pg_propgraph_property] = {0};
     932              :         HeapTuple   tup;
     933              :         ObjectAddress myself;
     934              :         ObjectAddress referenced;
     935              : 
     936          759 :         rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock);
     937              : 
     938          759 :         propoid = GetNewOidWithIndex(rel, PropgraphPropertyObjectIndexId, Anum_pg_propgraph_property_oid);
     939          759 :         values[Anum_pg_propgraph_property_oid - 1] = ObjectIdGetDatum(propoid);
     940          759 :         values[Anum_pg_propgraph_property_pgppgid - 1] = ObjectIdGetDatum(graphid);
     941          759 :         namestrcpy(&propnamedata, propname);
     942          759 :         values[Anum_pg_propgraph_property_pgpname - 1] = NameGetDatum(&propnamedata);
     943          759 :         values[Anum_pg_propgraph_property_pgptypid - 1] = ObjectIdGetDatum(exprtypid);
     944          759 :         values[Anum_pg_propgraph_property_pgptypmod - 1] = Int32GetDatum(exprtypmod);
     945          759 :         values[Anum_pg_propgraph_property_pgpcollation - 1] = ObjectIdGetDatum(exprcollation);
     946              : 
     947          759 :         tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
     948          759 :         CatalogTupleInsert(rel, tup);
     949          759 :         heap_freetuple(tup);
     950              : 
     951          759 :         ObjectAddressSet(myself, PropgraphPropertyRelationId, propoid);
     952              : 
     953          759 :         ObjectAddressSet(referenced, RelationRelationId, graphid);
     954          759 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     955          759 :         ObjectAddressSet(referenced, TypeRelationId, exprtypid);
     956          759 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     957          759 :         if (OidIsValid(exprcollation) && exprcollation != DEFAULT_COLLATION_OID)
     958              :         {
     959           23 :             ObjectAddressSet(referenced, CollationRelationId, exprcollation);
     960           23 :             recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     961              :         }
     962              : 
     963          759 :         table_close(rel, NoLock);
     964              :     }
     965              :     else
     966              :     {
     967          643 :         HeapTuple   pgptup = SearchSysCache1(PROPGRAPHPROPOID, ObjectIdGetDatum(propoid));
     968          643 :         Form_pg_propgraph_property pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup);
     969          643 :         Oid         proptypid = pgpform->pgptypid;
     970          643 :         int32       proptypmod = pgpform->pgptypmod;
     971          643 :         Oid         propcollation = pgpform->pgpcollation;
     972              : 
     973          643 :         ReleaseSysCache(pgptup);
     974              : 
     975              :         /*
     976              :          * Check that in the graph, all properties with the same name have the
     977              :          * same type (independent of which label they are on).  (See SQL/PGQ
     978              :          * subclause "Consistency check of a tabular property graph
     979              :          * descriptor".)
     980              :          */
     981          643 :         if (proptypid != exprtypid || proptypmod != exprtypmod)
     982              :         {
     983           16 :             ereport(ERROR,
     984              :                     errcode(ERRCODE_SYNTAX_ERROR),
     985              :                     errmsg("property \"%s\" data type mismatch: %s vs. %s",
     986              :                            propname, format_type_with_typemod(proptypid, proptypmod), format_type_with_typemod(exprtypid, exprtypmod)),
     987              :                     errdetail("In a property graph, a property of the same name has to have the same data type in each label."));
     988              :         }
     989              : 
     990              :         /* Similarly for collation */
     991          627 :         if (propcollation != exprcollation)
     992              :         {
     993           16 :             ereport(ERROR,
     994              :                     errcode(ERRCODE_SYNTAX_ERROR),
     995              :                     errmsg("property \"%s\" collation mismatch: %s vs. %s",
     996              :                            propname, get_collation_name(propcollation), get_collation_name(exprcollation)),
     997              :                     errdetail("In a property graph, a property of the same name has to have the same collation in each label."));
     998              :         }
     999              :     }
    1000              : 
    1001              :     /*
    1002              :      * Insert into pg_propgraph_label_property
    1003              :      */
    1004              :     {
    1005              :         Relation    rel;
    1006         1370 :         Datum       values[Natts_pg_propgraph_label_property] = {0};
    1007         1370 :         bool        nulls[Natts_pg_propgraph_label_property] = {0};
    1008              :         Oid         plpoid;
    1009              :         HeapTuple   tup;
    1010              :         ObjectAddress myself;
    1011              :         ObjectAddress referenced;
    1012              : 
    1013         1370 :         rel = table_open(PropgraphLabelPropertyRelationId, RowExclusiveLock);
    1014              : 
    1015         1370 :         plpoid = GetNewOidWithIndex(rel, PropgraphLabelPropertyObjectIndexId, Anum_pg_propgraph_label_property_oid);
    1016         1370 :         values[Anum_pg_propgraph_label_property_oid - 1] = ObjectIdGetDatum(plpoid);
    1017         1370 :         values[Anum_pg_propgraph_label_property_plppropid - 1] = ObjectIdGetDatum(propoid);
    1018         1370 :         values[Anum_pg_propgraph_label_property_plpellabelid - 1] = ObjectIdGetDatum(ellabeloid);
    1019         1370 :         values[Anum_pg_propgraph_label_property_plpexpr - 1] = CStringGetTextDatum(nodeToString(expr));
    1020              : 
    1021         1370 :         tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
    1022         1370 :         CatalogTupleInsert(rel, tup);
    1023         1370 :         heap_freetuple(tup);
    1024              : 
    1025         1370 :         ObjectAddressSet(myself, PropgraphLabelPropertyRelationId, plpoid);
    1026              : 
    1027         1370 :         ObjectAddressSet(referenced, PropgraphPropertyRelationId, propoid);
    1028         1370 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
    1029              : 
    1030         1370 :         ObjectAddressSet(referenced, PropgraphElementLabelRelationId, ellabeloid);
    1031         1370 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
    1032              : 
    1033         1370 :         recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false);
    1034              : 
    1035         1370 :         table_close(rel, NoLock);
    1036              :     }
    1037         1370 : }
    1038              : 
    1039              : /*
    1040              :  * Check that for the given graph element, all properties with the same name
    1041              :  * have the same expression for each label.  (See SQL/PGQ subclause "Creation
    1042              :  * of an element table descriptor".)
    1043              :  *
    1044              :  * We check this after all the catalog records are already inserted.  This
    1045              :  * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER
    1046              :  * PROPERTY GRAPH.  We pass in the element OID so that ALTER PROPERTY GRAPH
    1047              :  * only has to check the element it has just operated on.  CREATE PROPERTY
    1048              :  * GROUP checks all elements it has created.
    1049              :  */
    1050              : static void
    1051          483 : check_element_properties(Oid peoid)
    1052              : {
    1053              :     Relation    rel1;
    1054              :     ScanKeyData key1[1];
    1055              :     SysScanDesc scan1;
    1056              :     HeapTuple   tuple1;
    1057          483 :     List       *propoids = NIL;
    1058          483 :     List       *propexprs = NIL;
    1059              : 
    1060          483 :     rel1 = table_open(PropgraphElementLabelRelationId, AccessShareLock);
    1061          483 :     ScanKeyInit(&key1[0],
    1062              :                 Anum_pg_propgraph_element_label_pgelelid,
    1063              :                 BTEqualStrategyNumber, F_OIDEQ,
    1064              :                 ObjectIdGetDatum(peoid));
    1065              : 
    1066          483 :     scan1 = systable_beginscan(rel1, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, key1);
    1067         1102 :     while (HeapTupleIsValid(tuple1 = systable_getnext(scan1)))
    1068              :     {
    1069          627 :         Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple1);
    1070              :         Relation    rel2;
    1071              :         ScanKeyData key2[1];
    1072              :         SysScanDesc scan2;
    1073              :         HeapTuple   tuple2;
    1074              : 
    1075          627 :         rel2 = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
    1076          627 :         ScanKeyInit(&key2[0],
    1077              :                     Anum_pg_propgraph_label_property_plpellabelid,
    1078              :                     BTEqualStrategyNumber, F_OIDEQ,
    1079              :                     ObjectIdGetDatum(ellabel->oid));
    1080              : 
    1081          627 :         scan2 = systable_beginscan(rel2, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key2);
    1082         1965 :         while (HeapTupleIsValid(tuple2 = systable_getnext(scan2)))
    1083              :         {
    1084         1346 :             Form_pg_propgraph_label_property lprop = (Form_pg_propgraph_label_property) GETSTRUCT(tuple2);
    1085              :             Oid         propoid;
    1086              :             Datum       datum;
    1087              :             bool        isnull;
    1088              :             char       *propexpr;
    1089              :             ListCell   *lc1,
    1090              :                        *lc2;
    1091              :             bool        found;
    1092              : 
    1093         1346 :             propoid = lprop->plppropid;
    1094         1346 :             datum = heap_getattr(tuple2, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(rel2), &isnull);
    1095              :             Assert(!isnull);
    1096         1346 :             propexpr = TextDatumGetCString(datum);
    1097              : 
    1098         1346 :             found = false;
    1099         2643 :             forboth(lc1, propoids, lc2, propexprs)
    1100              :             {
    1101         1400 :                 if (propoid == lfirst_oid(lc1))
    1102              :                 {
    1103              :                     Node       *na,
    1104              :                                *nb;
    1105              : 
    1106          103 :                     na = stringToNode(propexpr);
    1107          103 :                     nb = stringToNode(lfirst(lc2));
    1108              : 
    1109          103 :                     found = true;
    1110              : 
    1111          103 :                     if (!equal(na, nb))
    1112              :                     {
    1113              :                         HeapTuple   tuple3;
    1114              :                         Form_pg_propgraph_element elform;
    1115              :                         List       *dpcontext;
    1116              :                         char       *dpa,
    1117              :                                    *dpb;
    1118              : 
    1119            8 :                         tuple3 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peoid));
    1120            8 :                         if (!tuple3)
    1121            0 :                             elog(ERROR, "cache lookup failed for property graph element %u", peoid);
    1122            8 :                         elform = (Form_pg_propgraph_element) GETSTRUCT(tuple3);
    1123            8 :                         dpcontext = deparse_context_for(get_rel_name(elform->pgerelid), elform->pgerelid);
    1124              : 
    1125            8 :                         dpa = deparse_expression(na, dpcontext, false, false);
    1126            8 :                         dpb = deparse_expression(nb, dpcontext, false, false);
    1127              : 
    1128              :                         /*
    1129              :                          * show in sorted order to keep output independent of
    1130              :                          * index order
    1131              :                          */
    1132            8 :                         if (strcmp(dpa, dpb) > 0)
    1133              :                         {
    1134              :                             char       *tmp;
    1135              : 
    1136            0 :                             tmp = dpa;
    1137            0 :                             dpa = dpb;
    1138            0 :                             dpb = tmp;
    1139              :                         }
    1140              : 
    1141            8 :                         ereport(ERROR,
    1142              :                                 errcode(ERRCODE_SYNTAX_ERROR),
    1143              :                                 errmsg("element \"%s\" property \"%s\" expression mismatch: %s vs. %s",
    1144              :                                        NameStr(elform->pgealias), get_propgraph_property_name(propoid), dpa, dpb),
    1145              :                                 errdetail("In a property graph element, a property of the same name has to have the same expression in each label."));
    1146              : 
    1147              :                         ReleaseSysCache(tuple3);
    1148              :                     }
    1149              : 
    1150           95 :                     break;
    1151              :                 }
    1152              :             }
    1153              : 
    1154         1338 :             if (!found)
    1155              :             {
    1156         1243 :                 propoids = lappend_oid(propoids, propoid);
    1157         1243 :                 propexprs = lappend(propexprs, propexpr);
    1158              :             }
    1159              :         }
    1160          619 :         systable_endscan(scan2);
    1161          619 :         table_close(rel2, AccessShareLock);
    1162              :     }
    1163              : 
    1164          475 :     systable_endscan(scan1);
    1165          475 :     table_close(rel1, AccessShareLock);
    1166          475 : }
    1167              : 
    1168              : /*
    1169              :  * Check that for the given element label, all labels of the same name in the
    1170              :  * graph have the same number and names of properties (independent of which
    1171              :  * element they are on).  (See SQL/PGQ subclause "Consistency check of a
    1172              :  * tabular property graph descriptor".)
    1173              :  *
    1174              :  * We check this after all the catalog records are already inserted.  This
    1175              :  * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER
    1176              :  * PROPERTY GRAPH.  We pass in the element label OID so that some variants of
    1177              :  * ALTER PROPERTY GRAPH only have to check the element label it has just
    1178              :  * operated on.  CREATE PROPERTY GRAPH and other ALTER PROPERTY GRAPH variants
    1179              :  * check all labels.
    1180              :  */
    1181              : static void
    1182          955 : check_element_label_properties(Oid ellabeloid)
    1183              : {
    1184              :     Relation    rel;
    1185              :     SysScanDesc scan;
    1186              :     ScanKeyData key[1];
    1187              :     HeapTuple   tuple;
    1188          955 :     Oid         labelid = InvalidOid;
    1189          955 :     Oid         ref_ellabeloid = InvalidOid;
    1190              :     List       *myprops,
    1191              :                *refprops;
    1192              :     List       *diff1,
    1193              :                *diff2;
    1194              : 
    1195          955 :     rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
    1196              : 
    1197              :     /*
    1198              :      * Get element label info
    1199              :      */
    1200          955 :     ScanKeyInit(&key[0],
    1201              :                 Anum_pg_propgraph_element_label_oid,
    1202              :                 BTEqualStrategyNumber,
    1203              :                 F_OIDEQ, ObjectIdGetDatum(ellabeloid));
    1204          955 :     scan = systable_beginscan(rel, PropgraphElementLabelObjectIndexId, true, NULL, 1, key);
    1205          955 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1206              :     {
    1207          955 :         Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
    1208              : 
    1209          955 :         labelid = ellabel->pgellabelid;
    1210              :     }
    1211          955 :     systable_endscan(scan);
    1212          955 :     if (!labelid)
    1213            0 :         elog(ERROR, "element label %u not found", ellabeloid);
    1214              : 
    1215              :     /*
    1216              :      * Find a reference element label to fetch label properties.  The
    1217              :      * reference element label has to have the label OID as the one being
    1218              :      * checked but be distinct from the one being checked.
    1219              :      */
    1220          955 :     ScanKeyInit(&key[0],
    1221              :                 Anum_pg_propgraph_element_label_pgellabelid,
    1222              :                 BTEqualStrategyNumber,
    1223              :                 F_OIDEQ, ObjectIdGetDatum(labelid));
    1224          955 :     scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
    1225         1585 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1226              :     {
    1227         1084 :         Form_pg_propgraph_element_label otherellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
    1228              : 
    1229         1084 :         if (otherellabel->oid != ellabeloid)
    1230              :         {
    1231          454 :             ref_ellabeloid = otherellabel->oid;
    1232          454 :             break;
    1233              :         }
    1234              :     }
    1235          955 :     systable_endscan(scan);
    1236              : 
    1237          955 :     table_close(rel, AccessShareLock);
    1238              : 
    1239              :     /*
    1240              :      * If there is not previous definition of this label, then we are done.
    1241              :      */
    1242          955 :     if (!ref_ellabeloid)
    1243          501 :         return;
    1244              : 
    1245              :     /*
    1246              :      * Now check number and names.
    1247              :      *
    1248              :      * XXX We could provide more detail in the error messages, but that would
    1249              :      * probably only be useful for some ALTER commands, because otherwise it's
    1250              :      * not really clear which label definition is the wrong one, and so you'd
    1251              :      * have to construct a rather verbose report to be of any use.  Let's keep
    1252              :      * it simple for now.
    1253              :      */
    1254              : 
    1255          454 :     myprops = get_element_label_property_names(ellabeloid);
    1256          454 :     refprops = get_element_label_property_names(ref_ellabeloid);
    1257              : 
    1258          454 :     if (list_length(refprops) != list_length(myprops))
    1259           16 :         ereport(ERROR,
    1260              :                 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1261              :                 errmsg("mismatching number of properties in definition of label \"%s\"", get_propgraph_label_name(labelid)));
    1262              : 
    1263          438 :     diff1 = list_difference(myprops, refprops);
    1264          438 :     diff2 = list_difference(refprops, myprops);
    1265              : 
    1266          438 :     if (diff1 || diff2)
    1267            8 :         ereport(ERROR,
    1268              :                 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1269              :                 errmsg("mismatching properties names in definition of label \"%s\"", get_propgraph_label_name(labelid)));
    1270              : }
    1271              : 
    1272              : /*
    1273              :  * As above, but check all labels of a graph.
    1274              :  */
    1275              : static void
    1276          189 : check_all_labels_properties(Oid pgrelid)
    1277              : {
    1278          960 :     foreach_oid(labeloid, get_graph_label_ids(pgrelid))
    1279              :     {
    1280         2103 :         foreach_oid(ellabeloid, get_label_element_label_ids(labeloid))
    1281              :         {
    1282          915 :             check_element_label_properties(ellabeloid);
    1283              :         }
    1284              :     }
    1285          177 : }
    1286              : 
    1287              : /*
    1288              :  * ALTER PROPERTY GRAPH
    1289              :  */
    1290              : ObjectAddress
    1291          124 : AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt)
    1292              : {
    1293              :     Oid         pgrelid;
    1294              :     ListCell   *lc;
    1295              :     ObjectAddress pgaddress;
    1296              : 
    1297          124 :     pgrelid = RangeVarGetRelidExtended(stmt->pgname,
    1298              :                                        ShareRowExclusiveLock,
    1299          124 :                                        stmt->missing_ok ? RVR_MISSING_OK : 0,
    1300              :                                        RangeVarCallbackOwnsRelation,
    1301              :                                        NULL);
    1302          120 :     if (pgrelid == InvalidOid)
    1303              :     {
    1304            0 :         ereport(NOTICE,
    1305              :                 (errmsg("relation \"%s\" does not exist, skipping",
    1306              :                         stmt->pgname->relname)));
    1307            0 :         return InvalidObjectAddress;
    1308              :     }
    1309              : 
    1310          120 :     ObjectAddressSet(pgaddress, RelationRelationId, pgrelid);
    1311              : 
    1312          136 :     foreach(lc, stmt->add_vertex_tables)
    1313              :     {
    1314           32 :         PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
    1315              :         struct element_info *vinfo;
    1316              :         Relation    rel;
    1317              :         Oid         peoid;
    1318              : 
    1319           32 :         vinfo = palloc0_object(struct element_info);
    1320           32 :         vinfo->kind = PGEKIND_VERTEX;
    1321              : 
    1322           32 :         vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
    1323              : 
    1324           32 :         rel = table_open(vinfo->relid, NoLock);
    1325              : 
    1326           32 :         if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP)
    1327            4 :             ereport(ERROR,
    1328              :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1329              :                      errmsg("cannot add temporary element table to non-temporary property graph"),
    1330              :                      errdetail("Table \"%s\" is a temporary table.", get_rel_name(vinfo->relid)),
    1331              :                      parser_errposition(pstate, vertex->vtable->location)));
    1332              : 
    1333           28 :         if (vertex->vtable->alias)
    1334            4 :             vinfo->aliasname = vertex->vtable->alias->aliasname;
    1335              :         else
    1336           24 :             vinfo->aliasname = vertex->vtable->relname;
    1337              : 
    1338           28 :         vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location);
    1339              : 
    1340           28 :         vinfo->labels = vertex->labels;
    1341              : 
    1342           28 :         table_close(rel, NoLock);
    1343              : 
    1344           28 :         if (SearchSysCacheExists2(PROPGRAPHELALIAS,
    1345              :                                   ObjectIdGetDatum(pgrelid),
    1346              :                                   CStringGetDatum(vinfo->aliasname)))
    1347            4 :             ereport(ERROR,
    1348              :                     errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1349              :                     errmsg("alias \"%s\" already exists in property graph \"%s\"",
    1350              :                            vinfo->aliasname, stmt->pgname->relname),
    1351              :                     parser_errposition(pstate, vertex->vtable->location));
    1352              : 
    1353           24 :         peoid = insert_element_record(pgaddress, vinfo);
    1354              : 
    1355           20 :         CommandCounterIncrement();
    1356           20 :         check_element_properties(peoid);
    1357           16 :         check_all_labels_properties(pgrelid);
    1358              :     }
    1359              : 
    1360          136 :     foreach(lc, stmt->add_edge_tables)
    1361              :     {
    1362           36 :         PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc);
    1363              :         struct element_info *einfo;
    1364              :         Relation    rel;
    1365              :         Relation    srcrel;
    1366              :         Relation    destrel;
    1367              :         Oid         peoid;
    1368              : 
    1369           36 :         einfo = palloc0_object(struct element_info);
    1370           36 :         einfo->kind = PGEKIND_EDGE;
    1371              : 
    1372           36 :         einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
    1373              : 
    1374           36 :         rel = table_open(einfo->relid, NoLock);
    1375              : 
    1376           36 :         if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP)
    1377            0 :             ereport(ERROR,
    1378              :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1379              :                      errmsg("cannot add temporary element table to non-temporary property graph"),
    1380              :                      errdetail("Table \"%s\" is a temporary table.", get_rel_name(einfo->relid)),
    1381              :                      parser_errposition(pstate, edge->etable->location)));
    1382              : 
    1383           36 :         if (edge->etable->alias)
    1384            0 :             einfo->aliasname = edge->etable->alias->aliasname;
    1385              :         else
    1386           36 :             einfo->aliasname = edge->etable->relname;
    1387              : 
    1388           36 :         einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location);
    1389              : 
    1390           36 :         einfo->srcvertexid = get_vertex_oid(pstate, pgrelid, edge->esrcvertex, edge->location);
    1391           36 :         einfo->destvertexid = get_vertex_oid(pstate, pgrelid, edge->edestvertex, edge->location);
    1392              : 
    1393           36 :         einfo->srcrelid = get_element_relid(einfo->srcvertexid);
    1394           36 :         einfo->destrelid = get_element_relid(einfo->destvertexid);
    1395              : 
    1396           36 :         srcrel = table_open(einfo->srcrelid, AccessShareLock);
    1397           36 :         destrel = table_open(einfo->destrelid, AccessShareLock);
    1398              : 
    1399           36 :         propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel,
    1400           36 :                                     einfo->aliasname, edge->location, "SOURCE",
    1401              :                                     &einfo->srckey, &einfo->srcref, &einfo->srceqop);
    1402           36 :         propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel,
    1403           36 :                                     einfo->aliasname, edge->location, "DESTINATION",
    1404              :                                     &einfo->destkey, &einfo->destref, &einfo->desteqop);
    1405              : 
    1406           36 :         einfo->labels = edge->labels;
    1407              : 
    1408           36 :         table_close(destrel, NoLock);
    1409           36 :         table_close(srcrel, NoLock);
    1410              : 
    1411           36 :         table_close(rel, NoLock);
    1412              : 
    1413           36 :         if (SearchSysCacheExists2(PROPGRAPHELALIAS,
    1414              :                                   ObjectIdGetDatum(pgrelid),
    1415              :                                   CStringGetDatum(einfo->aliasname)))
    1416            0 :             ereport(ERROR,
    1417              :                     errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1418              :                     errmsg("alias \"%s\" already exists in property graph \"%s\"",
    1419              :                            einfo->aliasname, stmt->pgname->relname),
    1420              :                     parser_errposition(pstate, edge->etable->location));
    1421              : 
    1422           36 :         peoid = insert_element_record(pgaddress, einfo);
    1423              : 
    1424           32 :         CommandCounterIncrement();
    1425           32 :         check_element_properties(peoid);
    1426           32 :         check_all_labels_properties(pgrelid);
    1427              :     }
    1428              : 
    1429          104 :     foreach(lc, stmt->drop_vertex_tables)
    1430              :     {
    1431            8 :         char       *alias = strVal(lfirst(lc));
    1432              :         Oid         peoid;
    1433              :         ObjectAddress obj;
    1434              : 
    1435            8 :         peoid = get_vertex_oid(pstate, pgrelid, alias, -1);
    1436            8 :         ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
    1437            8 :         performDeletion(&obj, stmt->drop_behavior, 0);
    1438              :     }
    1439              : 
    1440          108 :     foreach(lc, stmt->drop_edge_tables)
    1441              :     {
    1442           12 :         char       *alias = strVal(lfirst(lc));
    1443              :         Oid         peoid;
    1444              :         ObjectAddress obj;
    1445              : 
    1446           12 :         peoid = get_edge_oid(pstate, pgrelid, alias, -1);
    1447           12 :         ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
    1448           12 :         performDeletion(&obj, stmt->drop_behavior, 0);
    1449              :     }
    1450              : 
    1451              :     /* Remove any orphaned pg_propgraph_label entries */
    1452           96 :     if (stmt->drop_vertex_tables || stmt->drop_edge_tables)
    1453              :     {
    1454           88 :         foreach_oid(labeloid, get_graph_label_ids(pgrelid))
    1455              :         {
    1456           64 :             if (!get_label_element_label_ids(labeloid))
    1457              :             {
    1458              :                 ObjectAddress obj;
    1459              : 
    1460           12 :                 ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid);
    1461           12 :                 performDeletion(&obj, stmt->drop_behavior, 0);
    1462              :             }
    1463              :         }
    1464              :     }
    1465              : 
    1466          108 :     foreach(lc, stmt->add_labels)
    1467              :     {
    1468           28 :         PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
    1469              :         Oid         peoid;
    1470              :         Oid         pgerelid;
    1471              :         Oid         ellabeloid;
    1472              : 
    1473              :         Assert(lp->label);
    1474              : 
    1475           28 :         if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
    1476           28 :             peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
    1477              :         else
    1478            0 :             peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
    1479              : 
    1480           28 :         pgerelid = get_element_relid(peoid);
    1481              : 
    1482           28 :         ellabeloid = insert_label_record(pgrelid, peoid, lp->label);
    1483           28 :         insert_property_records(pgrelid, ellabeloid, pgerelid, lp->properties);
    1484              : 
    1485           24 :         CommandCounterIncrement();
    1486           24 :         check_element_properties(peoid);
    1487           24 :         check_element_label_properties(ellabeloid);
    1488              :     }
    1489              : 
    1490           80 :     if (stmt->drop_label)
    1491              :     {
    1492              :         Oid         peoid;
    1493              :         Oid         labeloid;
    1494              :         Oid         ellabeloid;
    1495              :         ObjectAddress obj;
    1496              : 
    1497           12 :         if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
    1498           12 :             peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
    1499              :         else
    1500            0 :             peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
    1501              : 
    1502           12 :         labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
    1503              :                                    Anum_pg_propgraph_label_oid,
    1504              :                                    ObjectIdGetDatum(pgrelid),
    1505              :                                    CStringGetDatum(stmt->drop_label));
    1506           12 :         if (!labeloid)
    1507            4 :             ereport(ERROR,
    1508              :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1509              :                     errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
    1510              :                            get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
    1511              :                     parser_errposition(pstate, -1));
    1512              : 
    1513            8 :         ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
    1514              :                                      Anum_pg_propgraph_element_label_oid,
    1515              :                                      ObjectIdGetDatum(peoid),
    1516              :                                      ObjectIdGetDatum(labeloid));
    1517              : 
    1518            8 :         if (!ellabeloid)
    1519            0 :             ereport(ERROR,
    1520              :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1521              :                     errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
    1522              :                            get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
    1523              :                     parser_errposition(pstate, -1));
    1524              : 
    1525            8 :         ObjectAddressSet(obj, PropgraphElementLabelRelationId, ellabeloid);
    1526            8 :         performDeletion(&obj, stmt->drop_behavior, 0);
    1527              : 
    1528              :         /* Remove any orphaned pg_propgraph_label entries */
    1529            8 :         if (!get_label_element_label_ids(labeloid))
    1530              :         {
    1531            4 :             ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid);
    1532            4 :             performDeletion(&obj, stmt->drop_behavior, 0);
    1533              :         }
    1534              :     }
    1535              : 
    1536           76 :     if (stmt->add_properties)
    1537              :     {
    1538              :         Oid         peoid;
    1539              :         Oid         pgerelid;
    1540              :         Oid         labeloid;
    1541              :         Oid         ellabeloid;
    1542              : 
    1543            8 :         if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
    1544            4 :             peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
    1545              :         else
    1546            4 :             peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
    1547              : 
    1548            8 :         labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
    1549              :                                    Anum_pg_propgraph_label_oid,
    1550              :                                    ObjectIdGetDatum(pgrelid),
    1551              :                                    CStringGetDatum(stmt->alter_label));
    1552            8 :         if (!labeloid)
    1553            0 :             ereport(ERROR,
    1554              :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1555              :                     errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
    1556              :                            get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
    1557              :                     parser_errposition(pstate, -1));
    1558              : 
    1559            8 :         ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
    1560              :                                      Anum_pg_propgraph_element_label_oid,
    1561              :                                      ObjectIdGetDatum(peoid),
    1562              :                                      ObjectIdGetDatum(labeloid));
    1563            8 :         if (!ellabeloid)
    1564            0 :             ereport(ERROR,
    1565              :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1566              :                     errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
    1567              :                            get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
    1568              :                     parser_errposition(pstate, -1));
    1569              : 
    1570            8 :         pgerelid = get_element_relid(peoid);
    1571              : 
    1572            8 :         insert_property_records(pgrelid, ellabeloid, pgerelid, stmt->add_properties);
    1573              : 
    1574            8 :         CommandCounterIncrement();
    1575            8 :         check_element_properties(peoid);
    1576            8 :         check_element_label_properties(ellabeloid);
    1577              :     }
    1578              : 
    1579           76 :     if (stmt->drop_properties)
    1580              :     {
    1581              :         Oid         peoid;
    1582              :         Oid         labeloid;
    1583              :         Oid         ellabeloid;
    1584              :         ObjectAddress obj;
    1585              : 
    1586            8 :         if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
    1587            4 :             peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
    1588              :         else
    1589            4 :             peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
    1590              : 
    1591            8 :         labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
    1592              :                                    Anum_pg_propgraph_label_oid,
    1593              :                                    ObjectIdGetDatum(pgrelid),
    1594              :                                    CStringGetDatum(stmt->alter_label));
    1595            8 :         if (!labeloid)
    1596            0 :             ereport(ERROR,
    1597              :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1598              :                     errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
    1599              :                            get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
    1600              :                     parser_errposition(pstate, -1));
    1601              : 
    1602            8 :         ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
    1603              :                                      Anum_pg_propgraph_element_label_oid,
    1604              :                                      ObjectIdGetDatum(peoid),
    1605              :                                      ObjectIdGetDatum(labeloid));
    1606              : 
    1607            8 :         if (!ellabeloid)
    1608            0 :             ereport(ERROR,
    1609              :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1610              :                     errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
    1611              :                            get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
    1612              :                     parser_errposition(pstate, -1));
    1613              : 
    1614           16 :         foreach(lc, stmt->drop_properties)
    1615              :         {
    1616            8 :             char       *propname = strVal(lfirst(lc));
    1617              :             Oid         propoid;
    1618              :             Oid         plpoid;
    1619              : 
    1620            8 :             propoid = GetSysCacheOid2(PROPGRAPHPROPNAME,
    1621              :                                       Anum_pg_propgraph_property_oid,
    1622              :                                       ObjectIdGetDatum(pgrelid),
    1623              :                                       CStringGetDatum(propname));
    1624            8 :             if (!propoid)
    1625            0 :                 ereport(ERROR,
    1626              :                         errcode(ERRCODE_UNDEFINED_OBJECT),
    1627              :                         errmsg("property graph \"%s\" element \"%s\" label \"%s\" has no property \"%s\"",
    1628              :                                get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label, propname),
    1629              :                         parser_errposition(pstate, -1));
    1630              : 
    1631            8 :             plpoid = GetSysCacheOid2(PROPGRAPHLABELPROP, Anum_pg_propgraph_label_property_oid, ObjectIdGetDatum(ellabeloid), ObjectIdGetDatum(propoid));
    1632              : 
    1633            8 :             ObjectAddressSet(obj, PropgraphLabelPropertyRelationId, plpoid);
    1634            8 :             performDeletion(&obj, stmt->drop_behavior, 0);
    1635              :         }
    1636              : 
    1637            8 :         check_element_label_properties(ellabeloid);
    1638              :     }
    1639              : 
    1640              :     /* Remove any orphaned pg_propgraph_property entries */
    1641           76 :     if (stmt->drop_properties || stmt->drop_vertex_tables || stmt->drop_edge_tables)
    1642              :     {
    1643          200 :         foreach_oid(propoid, get_graph_property_ids(pgrelid))
    1644              :         {
    1645              :             Relation    rel;
    1646              :             SysScanDesc scan;
    1647              :             ScanKeyData key[1];
    1648              : 
    1649          160 :             rel = table_open(PropgraphLabelPropertyRelationId, RowShareLock);
    1650          160 :             ScanKeyInit(&key[0],
    1651              :                         Anum_pg_propgraph_label_property_plppropid,
    1652              :                         BTEqualStrategyNumber, F_OIDEQ,
    1653              :                         ObjectIdGetDatum(propoid));
    1654              :             /* XXX no suitable index */
    1655          160 :             scan = systable_beginscan(rel, InvalidOid, true, NULL, 1, key);
    1656          160 :             if (!systable_getnext(scan))
    1657              :             {
    1658              :                 ObjectAddress obj;
    1659              : 
    1660           20 :                 ObjectAddressSet(obj, PropgraphPropertyRelationId, propoid);
    1661           20 :                 performDeletion(&obj, stmt->drop_behavior, 0);
    1662              :             }
    1663              : 
    1664          160 :             systable_endscan(scan);
    1665          160 :             table_close(rel, RowShareLock);
    1666              :         }
    1667              :     }
    1668              : 
    1669              :     /*
    1670              :      * Invalidate relcache entry of the property graph so that the queries in
    1671              :      * the cached plans referencing the property graph will be rewritten
    1672              :      * considering changes to the propert graph.
    1673              :      */
    1674           76 :     CacheInvalidateRelcacheByRelid(pgrelid);
    1675              : 
    1676           76 :     return pgaddress;
    1677              : }
    1678              : 
    1679              : /*
    1680              :  * Get OID of vertex from graph OID and element alias.  Element must be a
    1681              :  * vertex, otherwise error.
    1682              :  */
    1683              : static Oid
    1684          128 : get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
    1685              : {
    1686              :     HeapTuple   tuple;
    1687              :     Oid         peoid;
    1688              : 
    1689          128 :     tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
    1690          128 :     if (!tuple)
    1691            0 :         ereport(ERROR,
    1692              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
    1693              :                 errmsg("property graph \"%s\" has no element with alias \"%s\"",
    1694              :                        get_rel_name(pgrelid), alias),
    1695              :                 parser_errposition(pstate, location));
    1696              : 
    1697          128 :     if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_VERTEX)
    1698            0 :         ereport(ERROR,
    1699              :                 errcode(ERRCODE_SYNTAX_ERROR),
    1700              :                 errmsg("element \"%s\" of property graph \"%s\" is not a vertex",
    1701              :                        alias, get_rel_name(pgrelid)),
    1702              :                 parser_errposition(pstate, location));
    1703              : 
    1704          128 :     peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
    1705              : 
    1706          128 :     ReleaseSysCache(tuple);
    1707              : 
    1708          128 :     return peoid;
    1709              : }
    1710              : 
    1711              : /*
    1712              :  * Get OID of edge from graph OID and element alias.  Element must be an edge,
    1713              :  * otherwise error.
    1714              :  */
    1715              : static Oid
    1716           20 : get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
    1717              : {
    1718              :     HeapTuple   tuple;
    1719              :     Oid         peoid;
    1720              : 
    1721           20 :     tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
    1722           20 :     if (!tuple)
    1723            0 :         ereport(ERROR,
    1724              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
    1725              :                 errmsg("property graph \"%s\" has no element with alias \"%s\"",
    1726              :                        get_rel_name(pgrelid), alias),
    1727              :                 parser_errposition(pstate, location));
    1728              : 
    1729           20 :     if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_EDGE)
    1730            0 :         ereport(ERROR,
    1731              :                 errcode(ERRCODE_SYNTAX_ERROR),
    1732              :                 errmsg("element \"%s\" of property graph \"%s\" is not an edge",
    1733              :                        alias, get_rel_name(pgrelid)),
    1734              :                 parser_errposition(pstate, location));
    1735              : 
    1736           20 :     peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
    1737              : 
    1738           20 :     ReleaseSysCache(tuple);
    1739              : 
    1740           20 :     return peoid;
    1741              : }
    1742              : 
    1743              : /*
    1744              :  * Get the element table relation OID from the OID of the element.
    1745              :  */
    1746              : static Oid
    1747          108 : get_element_relid(Oid peid)
    1748              : {
    1749              :     HeapTuple   tuple;
    1750              :     Oid         pgerelid;
    1751              : 
    1752          108 :     tuple = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peid));
    1753          108 :     if (!tuple)
    1754            0 :         elog(ERROR, "cache lookup failed for property graph element %u", peid);
    1755              : 
    1756          108 :     pgerelid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgerelid;
    1757              : 
    1758          108 :     ReleaseSysCache(tuple);
    1759              : 
    1760          108 :     return pgerelid;
    1761              : }
    1762              : 
    1763              : /*
    1764              :  * Get a list of all label OIDs of a graph.
    1765              :  */
    1766              : static List *
    1767          201 : get_graph_label_ids(Oid graphid)
    1768              : {
    1769              :     Relation    rel;
    1770              :     SysScanDesc scan;
    1771              :     ScanKeyData key[1];
    1772              :     HeapTuple   tuple;
    1773          201 :     List       *result = NIL;
    1774              : 
    1775          201 :     rel = table_open(PropgraphLabelRelationId, AccessShareLock);
    1776          201 :     ScanKeyInit(&key[0],
    1777              :                 Anum_pg_propgraph_label_pglpgid,
    1778              :                 BTEqualStrategyNumber,
    1779              :                 F_OIDEQ, ObjectIdGetDatum(graphid));
    1780          201 :     scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId, true, NULL, 1, key);
    1781          871 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1782              :     {
    1783          670 :         result = lappend_oid(result, ((Form_pg_propgraph_label) GETSTRUCT(tuple))->oid);
    1784              :     }
    1785          201 :     systable_endscan(scan);
    1786          201 :     table_close(rel, AccessShareLock);
    1787              : 
    1788          201 :     return result;
    1789              : }
    1790              : 
    1791              : /*
    1792              :  * Get a list of all element label OIDs for a label.
    1793              :  */
    1794              : static List *
    1795          678 : get_label_element_label_ids(Oid labelid)
    1796              : {
    1797              :     Relation    rel;
    1798              :     SysScanDesc scan;
    1799              :     ScanKeyData key[1];
    1800              :     HeapTuple   tuple;
    1801          678 :     List       *result = NIL;
    1802              : 
    1803          678 :     rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
    1804          678 :     ScanKeyInit(&key[0],
    1805              :                 Anum_pg_propgraph_element_label_pgellabelid,
    1806              :                 BTEqualStrategyNumber,
    1807              :                 F_OIDEQ, ObjectIdGetDatum(labelid));
    1808          678 :     scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
    1809         1721 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1810              :     {
    1811         1043 :         result = lappend_oid(result, ((Form_pg_propgraph_element_label) GETSTRUCT(tuple))->oid);
    1812              :     }
    1813          678 :     systable_endscan(scan);
    1814          678 :     table_close(rel, AccessShareLock);
    1815              : 
    1816          678 :     return result;
    1817              : }
    1818              : 
    1819              : /*
    1820              :  * Get the names of properties associated with the given element label OID.
    1821              :  *
    1822              :  * The result is a list of String nodes (so we can use list functions to
    1823              :  * detect differences).
    1824              :  */
    1825              : static List *
    1826          908 : get_element_label_property_names(Oid ellabeloid)
    1827              : {
    1828              :     Relation    rel;
    1829              :     SysScanDesc scan;
    1830              :     ScanKeyData key[1];
    1831              :     HeapTuple   tuple;
    1832          908 :     List       *result = NIL;
    1833              : 
    1834          908 :     rel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
    1835              : 
    1836          908 :     ScanKeyInit(&key[0],
    1837              :                 Anum_pg_propgraph_label_property_plpellabelid,
    1838              :                 BTEqualStrategyNumber, F_OIDEQ,
    1839              :                 ObjectIdGetDatum(ellabeloid));
    1840              : 
    1841          908 :     scan = systable_beginscan(rel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key);
    1842              : 
    1843         2350 :     while ((tuple = systable_getnext(scan)))
    1844              :     {
    1845         1442 :         Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tuple);
    1846              : 
    1847         1442 :         result = lappend(result, makeString(get_propgraph_property_name(plpform->plppropid)));
    1848              :     }
    1849              : 
    1850          908 :     systable_endscan(scan);
    1851          908 :     table_close(rel, AccessShareLock);
    1852              : 
    1853          908 :     return result;
    1854              : }
    1855              : 
    1856              : /*
    1857              :  * Get a list of all property OIDs of a graph.
    1858              :  */
    1859              : static List *
    1860           20 : get_graph_property_ids(Oid graphid)
    1861              : {
    1862              :     Relation    rel;
    1863              :     SysScanDesc scan;
    1864              :     ScanKeyData key[1];
    1865              :     HeapTuple   tuple;
    1866           20 :     List       *result = NIL;
    1867              : 
    1868           20 :     rel = table_open(PropgraphPropertyRelationId, AccessShareLock);
    1869           20 :     ScanKeyInit(&key[0],
    1870              :                 Anum_pg_propgraph_property_pgppgid,
    1871              :                 BTEqualStrategyNumber,
    1872              :                 F_OIDEQ, ObjectIdGetDatum(graphid));
    1873           20 :     scan = systable_beginscan(rel, PropgraphPropertyNameIndexId, true, NULL, 1, key);
    1874          180 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1875              :     {
    1876          160 :         result = lappend_oid(result, ((Form_pg_propgraph_property) GETSTRUCT(tuple))->oid);
    1877              :     }
    1878           20 :     systable_endscan(scan);
    1879           20 :     table_close(rel, AccessShareLock);
    1880              : 
    1881           20 :     return result;
    1882              : }
        

Generated by: LCOV version 2.0-1