LCOV - code coverage report
Current view: top level - src/backend/commands - propgraphcmds.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 94.7 % 719 681
Test Date: 2026-09-05 06:15:58 Functions: 100.0 % 22 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 77.0 % 508 391

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

Generated by: LCOV version 2.0-1