LCOV - code coverage report
Current view: top level - src/backend/rewrite - rewriteGraphTable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 97.7 % 398 389
Test Date: 2026-08-15 11:15:50 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 80.9 % 304 246

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * rewriteGraphTable.c
       4                 :             :  *      Support for rewriting GRAPH_TABLE clauses.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  * IDENTIFICATION
      10                 :             :  *    src/backend/rewrite/rewriteGraphTable.c
      11                 :             :  *
      12                 :             :  *-------------------------------------------------------------------------
      13                 :             :  */
      14                 :             : #include "postgres.h"
      15                 :             : 
      16                 :             : #include "access/genam.h"
      17                 :             : #include "access/sysattr.h"
      18                 :             : #include "access/table.h"
      19                 :             : #include "access/htup_details.h"
      20                 :             : #include "catalog/pg_operator.h"
      21                 :             : #include "catalog/pg_propgraph_element.h"
      22                 :             : #include "catalog/pg_propgraph_element_label.h"
      23                 :             : #include "catalog/pg_propgraph_label.h"
      24                 :             : #include "catalog/pg_propgraph_label_property.h"
      25                 :             : #include "catalog/pg_propgraph_property.h"
      26                 :             : #include "miscadmin.h"
      27                 :             : #include "nodes/makefuncs.h"
      28                 :             : #include "nodes/nodeFuncs.h"
      29                 :             : #include "optimizer/optimizer.h"
      30                 :             : #include "parser/analyze.h"
      31                 :             : #include "parser/parse_collate.h"
      32                 :             : #include "parser/parse_func.h"
      33                 :             : #include "parser/parse_node.h"
      34                 :             : #include "parser/parse_oper.h"
      35                 :             : #include "parser/parse_relation.h"
      36                 :             : #include "parser/parsetree.h"
      37                 :             : #include "parser/parse_graphtable.h"
      38                 :             : #include "rewrite/rewriteGraphTable.h"
      39                 :             : #include "rewrite/rewriteHandler.h"
      40                 :             : #include "rewrite/rewriteManip.h"
      41                 :             : #include "utils/array.h"
      42                 :             : #include "utils/builtins.h"
      43                 :             : #include "utils/fmgroids.h"
      44                 :             : #include "utils/lsyscache.h"
      45                 :             : #include "utils/ruleutils.h"
      46                 :             : #include "utils/syscache.h"
      47                 :             : 
      48                 :             : 
      49                 :             : /*
      50                 :             :  * Represents one path factor in a path.
      51                 :             :  *
      52                 :             :  * In a non-cyclic path, one path factor corresponds to one element pattern.
      53                 :             :  *
      54                 :             :  * In a cyclic path, one path factor corresponds to all the element patterns with
      55                 :             :  * the same variable name.
      56                 :             :  */
      57                 :             : struct path_factor
      58                 :             : {
      59                 :             :     GraphElementPatternKind kind;
      60                 :             :     const char *variable;
      61                 :             :     Node       *labelexpr;
      62                 :             :     Node       *whereClause;
      63                 :             :     int         factorpos;      /* Position of this path factor in the list of
      64                 :             :                                  * path factors representing a given path
      65                 :             :                                  * pattern. */
      66                 :             :     List       *labeloids;      /* OIDs of all the labels referenced in
      67                 :             :                                  * labelexpr. */
      68                 :             :     /* Links to adjacent vertex path factors if this is an edge path factor. */
      69                 :             :     struct path_factor *src_pf;
      70                 :             :     struct path_factor *dest_pf;
      71                 :             : };
      72                 :             : 
      73                 :             : /*
      74                 :             :  * Represents one property graph element (vertex or edge) in the path.
      75                 :             :  *
      76                 :             :  * Label expression in an element pattern resolves into a set of elements. We
      77                 :             :  * create one path_element object for each of those elements.
      78                 :             :  */
      79                 :             : struct path_element
      80                 :             : {
      81                 :             :     /* Path factor from which this element is derived. */
      82                 :             :     struct path_factor *path_factor;
      83                 :             :     Oid         elemoid;
      84                 :             :     Oid         reloid;
      85                 :             :     /* Source and destination vertex elements for an edge element. */
      86                 :             :     Oid         srcvertexid;
      87                 :             :     Oid         destvertexid;
      88                 :             :     /* Source and destination conditions for an edge element. */
      89                 :             :     List       *src_quals;
      90                 :             :     List       *dest_quals;
      91                 :             : };
      92                 :             : 
      93                 :             : static Node *replace_property_refs(Oid propgraphid, Node *node, const List *mappings);
      94                 :             : static List *build_edge_vertex_link_quals(HeapTuple edgetup, int edgerti, int refrti, Oid refid, AttrNumber catalog_key_attnum, AttrNumber catalog_ref_attnum, AttrNumber catalog_eqop_attnum);
      95                 :             : static List *generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern);
      96                 :             : static Query *generate_query_for_graph_path(RangeTblEntry *rte, List *graph_path);
      97                 :             : static Node *generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist);
      98                 :             : static List *generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, List *cur_path, List *path_elem_lists, int elempos);
      99                 :             : static Query *generate_query_for_empty_path_pattern(RangeTblEntry *rte);
     100                 :             : static Query *generate_union_from_pathqueries(List **pathqueries);
     101                 :             : static List *get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf);
     102                 :             : static bool is_property_associated_with_label(Oid labeloid, Oid propoid);
     103                 :             : static Node *get_element_property_expr(Oid elemoid, Oid propoid, int rtindex);
     104                 :             : 
     105                 :             : /*
     106                 :             :  * Convert GRAPH_TABLE clause into a subquery using relational
     107                 :             :  * operators.
     108                 :             :  */
     109                 :             : Query *
     110                 :         513 : rewriteGraphTable(Query *parsetree, int rt_index)
     111                 :             : {
     112                 :             :     RangeTblEntry *rte;
     113                 :             :     Query      *graph_table_query;
     114                 :             :     List       *path_pattern;
     115                 :         513 :     List       *pathqueries = NIL;
     116                 :             : 
     117                 :         513 :     rte = rt_fetch(rt_index, parsetree->rtable);
     118                 :             : 
     119                 :             :     Assert(list_length(rte->graph_pattern->path_pattern_list) == 1);
     120                 :             : 
     121                 :         513 :     path_pattern = linitial(rte->graph_pattern->path_pattern_list);
     122                 :         513 :     pathqueries = generate_queries_for_path_pattern(rte, path_pattern);
     123                 :         473 :     graph_table_query = generate_union_from_pathqueries(&pathqueries);
     124                 :             : 
     125                 :         473 :     AcquireRewriteLocks(graph_table_query, true, false);
     126                 :             : 
     127                 :         473 :     rte->rtekind = RTE_SUBQUERY;
     128                 :         473 :     rte->subquery = graph_table_query;
     129                 :         473 :     rte->lateral = true;
     130                 :             : 
     131                 :             :     /*
     132                 :             :      * Reset no longer applicable fields, to appease
     133                 :             :      * WRITE_READ_PARSE_PLAN_TREES.
     134                 :             :      */
     135                 :         473 :     rte->graph_pattern = NULL;
     136                 :         473 :     rte->graph_table_columns = NIL;
     137                 :             : 
     138                 :         473 :     return parsetree;
     139                 :             : }
     140                 :             : 
     141                 :             : /*
     142                 :             :  * Generate queries representing the given path pattern applied to the given
     143                 :             :  * property graph.
     144                 :             :  *
     145                 :             :  * A path pattern consists of one or more element patterns. Each of the element
     146                 :             :  * patterns may be satisfied by multiple elements. A path satisfying the given
     147                 :             :  * path pattern consists of one element from each element pattern.  There can be
     148                 :             :  * as many paths as the number of combinations of the elements.  A path pattern
     149                 :             :  * in itself is a K-partite graph where K = number of element patterns in the
     150                 :             :  * path pattern. The possible paths are computed by performing a DFS in this
     151                 :             :  * graph. The DFS is implemented as recursion. Each of these paths is converted
     152                 :             :  * into a query connecting all the elements in that path. Set of these queries is
     153                 :             :  * returned.
     154                 :             :  *
     155                 :             :  * Between every two vertex elements in the path there is an edge element that
     156                 :             :  * connects them.  An edge connects two vertices identified by the source and
     157                 :             :  * destination keys respectively. The connection between an edge and its
     158                 :             :  * adjacent vertex is naturally computed as an equi-join between edge and vertex
     159                 :             :  * table on their respective keys. Hence the query representing one path
     160                 :             :  * consists of JOINs between edge and vertex tables.
     161                 :             :  *
     162                 :             :  * generate_queries_for_path_pattern() starts the recursion but actual work is
     163                 :             :  * done by generate_queries_for_path_pattern_recurse().
     164                 :             :  * generate_query_for_graph_path() constructs a query for a given path.
     165                 :             :  *
     166                 :             :  * A path pattern may end up producing no path if any of the element patterns
     167                 :             :  * yields no elements or the edge patterns yield no edges connecting adjacent
     168                 :             :  * vertex patterns.  In such a case a dummy query which returns no result is
     169                 :             :  * returned (generate_query_for_empty_path_pattern()).
     170                 :             :  *
     171                 :             :  * 'path_pattern' is given path pattern to be applied on the property graph in
     172                 :             :  * the GRAPH_TABLE clause represented by given 'rte'.
     173                 :             :  */
     174                 :             : static List *
     175                 :         513 : generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern)
     176                 :             : {
     177                 :         513 :     List       *pathqueries = NIL;
     178                 :         513 :     List       *path_elem_lists = NIL;
     179                 :         513 :     int         factorpos = 0;
     180                 :         513 :     List       *path_factors = NIL;
     181                 :         513 :     struct path_factor *prev_pf = NULL;
     182                 :             : 
     183                 :             :     Assert(list_length(path_pattern) > 0);
     184                 :             : 
     185                 :             :     /*
     186                 :             :      * Create a list of path factors representing the given path pattern
     187                 :             :      * linking edge path factors to their adjacent vertex path factors.
     188                 :             :      *
     189                 :             :      * While doing that merge element patterns with the same variable name
     190                 :             :      * into a single path_factor.
     191                 :             :      */
     192   [ +  -  +  +  :        2563 :     foreach_node(GraphElementPattern, gep, path_pattern)
                   +  + ]
     193                 :             :     {
     194                 :        1561 :         struct path_factor *pf = NULL;
     195                 :             : 
     196                 :             :         /*
     197                 :             :          * Unsupported conditions should have been caught by the parser
     198                 :             :          * itself. We have corresponding Asserts here to document the
     199                 :             :          * assumptions in this code.
     200                 :             :          */
     201                 :             :         Assert(gep->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(gep->kind));
     202                 :             :         Assert(!gep->quantifier);
     203                 :             : 
     204   [ +  +  +  +  :        4678 :         foreach_ptr(struct path_factor, other, path_factors)
                   +  + ]
     205                 :             :         {
     206   [ +  +  +  + ]:        1716 :             if (gep->variable && other->variable &&
     207         [ +  + ]:        1130 :                 strcmp(gep->variable, other->variable) == 0)
     208                 :             :             {
     209         [ +  + ]:         152 :                 if (other->kind != gep->kind)
     210         [ +  - ]:           4 :                     ereport(ERROR,
     211                 :             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     212                 :             :                              errmsg("element patterns with same variable name \"%s\" but different element pattern types",
     213                 :             :                                     gep->variable)));
     214                 :             : 
     215                 :             :                 /*
     216                 :             :                  * If both the element patterns have label expressions, they
     217                 :             :                  * need to be conjuncted, which is not supported right now.
     218                 :             :                  *
     219                 :             :                  * However, an empty label expression means all labels.
     220                 :             :                  * Conjunction of any label expression with all labels is the
     221                 :             :                  * expression itself. Hence if only one of the two element
     222                 :             :                  * patterns has a label expression use that expression.
     223                 :             :                  */
     224         [ +  + ]:         148 :                 if (!other->labelexpr)
     225                 :         136 :                     other->labelexpr = gep->labelexpr;
     226   [ +  +  +  - ]:          12 :                 else if (gep->labelexpr && !equal(other->labelexpr, gep->labelexpr))
     227         [ +  - ]:           4 :                     ereport(ERROR,
     228                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     229                 :             :                              errmsg("element patterns with same variable name \"%s\" but different label expressions are not supported",
     230                 :             :                                     gep->variable)));
     231                 :             : 
     232                 :             :                 /*
     233                 :             :                  * If two element patterns have the same variable name, they
     234                 :             :                  * represent the same set of graph elements and hence are
     235                 :             :                  * constrained by conditions from both the element patterns.
     236                 :             :                  */
     237         [ +  + ]:         144 :                 if (!other->whereClause)
     238                 :         128 :                     other->whereClause = gep->whereClause;
     239         [ +  + ]:          16 :                 else if (gep->whereClause)
     240                 :           8 :                     other->whereClause = (Node *) makeBoolExpr(AND_EXPR,
     241                 :           8 :                                                                list_make2(other->whereClause, gep->whereClause),
     242                 :             :                                                                -1);
     243                 :         144 :                 pf = other;
     244                 :         144 :                 break;
     245                 :             :             }
     246                 :             :         }
     247                 :             : 
     248         [ +  + ]:        1553 :         if (!pf)
     249                 :             :         {
     250                 :        1409 :             pf = palloc0_object(struct path_factor);
     251                 :        1409 :             pf->factorpos = factorpos++;
     252                 :        1409 :             pf->kind = gep->kind;
     253                 :        1409 :             pf->labelexpr = gep->labelexpr;
     254                 :        1409 :             pf->variable = gep->variable;
     255                 :        1409 :             pf->whereClause = gep->whereClause;
     256                 :             : 
     257                 :        1409 :             path_factors = lappend(path_factors, pf);
     258                 :             :         }
     259                 :             : 
     260                 :             :         /*
     261                 :             :          * Setup links to the previous path factor in the path.
     262                 :             :          *
     263                 :             :          * If the previous path factor represents an edge, this path factor
     264                 :             :          * represents an adjacent vertex; the source vertex for an edge
     265                 :             :          * pointing left or the destination vertex for an edge pointing right.
     266                 :             :          * If this path factor represents an edge, the previous path factor
     267                 :             :          * represents an adjacent vertex; source vertex for an edge pointing
     268                 :             :          * right or the destination vertex for an edge pointing left.
     269                 :             :          *
     270                 :             :          * Edge pointing in any direction is treated similar to that pointing
     271                 :             :          * in right direction here.  When constructing a query in
     272                 :             :          * generate_query_for_graph_path(), we will try links in both the
     273                 :             :          * directions.
     274                 :             :          *
     275                 :             :          * If multiple edge patterns share the same variable name, they
     276                 :             :          * constrain the adjacent vertex patterns since an edge can connect
     277                 :             :          * only one pair of vertices. These adjacent vertex patterns need to
     278                 :             :          * be merged even though they have different variables. Such element
     279                 :             :          * patterns form a walk of graph where vertex and edges are repeated.
     280                 :             :          * For example, in (a)-[b]->(c)<-[b]-(d), (a) and (d) represent the
     281                 :             :          * same vertex element. This is slightly harder to implement and
     282                 :             :          * probably less useful. Hence not supported for now.
     283                 :             :          */
     284         [ +  + ]:        1553 :         if (prev_pf)
     285                 :             :         {
     286   [ +  +  +  + ]:        1040 :             if (prev_pf->kind == EDGE_PATTERN_RIGHT || prev_pf->kind == EDGE_PATTERN_ANY)
     287                 :             :             {
     288                 :             :                 Assert(!IS_EDGE_PATTERN(pf->kind));
     289   [ +  +  -  + ]:         508 :                 if (prev_pf->dest_pf && prev_pf->dest_pf != pf)
     290         [ #  # ]:           0 :                     ereport(ERROR,
     291                 :             :                             errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     292                 :             :                             errmsg("an edge cannot connect more than two vertices even in a cyclic pattern"));
     293                 :         508 :                 prev_pf->dest_pf = pf;
     294                 :             :             }
     295         [ +  + ]:         532 :             else if (prev_pf->kind == EDGE_PATTERN_LEFT)
     296                 :             :             {
     297                 :             :                 Assert(!IS_EDGE_PATTERN(pf->kind));
     298   [ -  +  -  - ]:           8 :                 if (prev_pf->src_pf && prev_pf->src_pf != pf)
     299         [ #  # ]:           0 :                     ereport(ERROR,
     300                 :             :                             errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     301                 :             :                             errmsg("an edge cannot connect more than two vertices even in a cyclic pattern"));
     302                 :           8 :                 prev_pf->src_pf = pf;
     303                 :             :             }
     304                 :             :             else
     305                 :             :             {
     306                 :             :                 Assert(prev_pf->kind == VERTEX_PATTERN);
     307                 :             :                 Assert(IS_EDGE_PATTERN(pf->kind));
     308                 :             :             }
     309                 :             : 
     310   [ +  +  +  + ]:        1040 :             if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY)
     311                 :             :             {
     312                 :             :                 Assert(!IS_EDGE_PATTERN(prev_pf->kind));
     313   [ +  +  +  + ]:         516 :                 if (pf->src_pf && pf->src_pf != prev_pf)
     314         [ +  - ]:           4 :                     ereport(ERROR,
     315                 :             :                             errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     316                 :             :                             errmsg("an edge cannot connect more than two vertices even in a cyclic pattern"));
     317                 :         512 :                 pf->src_pf = prev_pf;
     318                 :             :             }
     319         [ +  + ]:         524 :             else if (pf->kind == EDGE_PATTERN_LEFT)
     320                 :             :             {
     321                 :             :                 Assert(!IS_EDGE_PATTERN(prev_pf->kind));
     322   [ -  +  -  - ]:           8 :                 if (pf->dest_pf && pf->dest_pf != prev_pf)
     323         [ #  # ]:           0 :                     ereport(ERROR,
     324                 :             :                             errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     325                 :             :                             errmsg("an edge cannot connect more than two vertices even in a cyclic pattern"));
     326                 :           8 :                 pf->dest_pf = prev_pf;
     327                 :             :             }
     328                 :             :             else
     329                 :             :             {
     330                 :             :                 Assert(pf->kind == VERTEX_PATTERN);
     331                 :             :                 Assert(IS_EDGE_PATTERN(prev_pf->kind));
     332                 :             :             }
     333                 :             :         }
     334                 :             : 
     335                 :        1549 :         prev_pf = pf;
     336                 :             :     }
     337                 :             : 
     338                 :             :     /*
     339                 :             :      * Collect list of elements for each path factor. Do this after all the
     340                 :             :      * edge links are setup correctly.
     341                 :             :      */
     342   [ +  -  +  +  :        2347 :     foreach_ptr(struct path_factor, pf, path_factors)
                   +  + ]
     343                 :        1353 :         path_elem_lists = lappend(path_elem_lists,
     344                 :        1361 :                                   get_path_elements_for_path_factor(rte->relid, pf));
     345                 :             : 
     346                 :         493 :     pathqueries = generate_queries_for_path_pattern_recurse(rte, pathqueries,
     347                 :             :                                                             NIL, path_elem_lists, 0);
     348         [ +  + ]:         473 :     if (!pathqueries)
     349                 :           4 :         pathqueries = list_make1(generate_query_for_empty_path_pattern(rte));
     350                 :             : 
     351                 :         473 :     return pathqueries;
     352                 :             : }
     353                 :             : 
     354                 :             : /*
     355                 :             :  * Recursive workhorse function of generate_queries_for_path_pattern().
     356                 :             :  *
     357                 :             :  * `elempos` is the position of the next element being added in the path being
     358                 :             :  * built.
     359                 :             :  */
     360                 :             : static List *
     361                 :       10311 : generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, List *cur_path, List *path_elem_lists, int elempos)
     362                 :             : {
     363                 :       10311 :     List       *path_elems = list_nth_node(List, path_elem_lists, elempos);
     364                 :             : 
     365                 :             :     /* Guard against stack overflow due to complex path patterns. */
     366                 :       10311 :     check_stack_depth();
     367                 :             : 
     368   [ +  -  +  +  :       53632 :     foreach_ptr(struct path_element, pe, path_elems)
                   +  + ]
     369                 :             :     {
     370         [ -  + ]:       33138 :         CHECK_FOR_INTERRUPTS();
     371                 :             : 
     372                 :             :         /* Update current path being built with current element. */
     373                 :       33138 :         cur_path = lappend(cur_path, pe);
     374                 :             : 
     375                 :             :         /*
     376                 :             :          * If this is the last element in the path, generate query for the
     377                 :             :          * completed path. Else recurse processing the next element.
     378                 :             :          */
     379         [ +  + ]:       33138 :         if (list_length(path_elem_lists) == list_length(cur_path))
     380                 :             :         {
     381                 :       23320 :             Query      *pathquery = generate_query_for_graph_path(rte, cur_path);
     382                 :             : 
     383                 :             :             Assert(elempos == list_length(path_elem_lists) - 1);
     384         [ +  + ]:       23300 :             if (pathquery)
     385                 :         856 :                 pathqueries = lappend(pathqueries, pathquery);
     386                 :             :         }
     387                 :             :         else
     388                 :        9818 :             pathqueries = generate_queries_for_path_pattern_recurse(rte, pathqueries,
     389                 :             :                                                                     cur_path,
     390                 :             :                                                                     path_elem_lists,
     391                 :             :                                                                     elempos + 1);
     392                 :             :         /* Make way for the next element at the same position. */
     393                 :       33074 :         cur_path = list_delete_last(cur_path);
     394                 :             :     }
     395                 :             : 
     396                 :       10247 :     return pathqueries;
     397                 :             : }
     398                 :             : 
     399                 :             : /*
     400                 :             :  * Construct a query representing given graph path.
     401                 :             :  *
     402                 :             :  * The query contains:
     403                 :             :  *
     404                 :             :  * 1. targetlist corresponding to the COLUMNS clause of GRAPH_TABLE clause
     405                 :             :  *
     406                 :             :  * 2. quals corresponding to the WHERE clause of individual elements, WHERE
     407                 :             :  * clause in GRAPH_TABLE clause and quals representing edge-vertex links.
     408                 :             :  *
     409                 :             :  * 3. fromlist containing all elements in the path
     410                 :             :  *
     411                 :             :  * The collations of property expressions are obtained from the catalog. The
     412                 :             :  * collations of expressions in COLUMNS and WHERE clauses are assigned before
     413                 :             :  * rewriting the graph table.  The collations of the edge-vertex link quals are
     414                 :             :  * assigned when crafting those quals. Thus everything in the query that requires
     415                 :             :  * collation assignment has been taken care of already. No separate collation
     416                 :             :  * assignment is required in this function.
     417                 :             :  *
     418                 :             :  * More details in the prologue of generate_queries_for_path_pattern().
     419                 :             :  */
     420                 :             : static Query *
     421                 :       23320 : generate_query_for_graph_path(RangeTblEntry *rte, List *graph_path)
     422                 :             : {
     423                 :       23320 :     Query      *path_query = makeNode(Query);
     424                 :       23320 :     List       *fromlist = NIL;
     425                 :       23320 :     List       *qual_exprs = NIL;
     426                 :             :     List       *vars;
     427                 :             : 
     428                 :       23320 :     path_query->commandType = CMD_SELECT;
     429                 :             : 
     430   [ +  -  +  +  :       53608 :     foreach_ptr(struct path_element, pe, graph_path)
                   +  + ]
     431                 :             :     {
     432                 :       51856 :         struct path_factor *pf = pe->path_factor;
     433                 :             :         RangeTblRef *rtr;
     434                 :             :         Relation    rel;
     435                 :             :         ParseNamespaceItem *pni;
     436                 :             : 
     437                 :             :         Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
     438                 :             : 
     439                 :             :         /* Add conditions representing edge connections. */
     440   [ +  +  +  +  :       51856 :         if (IS_EDGE_PATTERN(pf->kind))
                   +  + ]
     441                 :             :         {
     442                 :             :             struct path_element *src_pe;
     443                 :             :             struct path_element *dest_pe;
     444                 :       25552 :             Expr       *edge_qual = NULL;
     445                 :             : 
     446                 :             :             Assert(pf->src_pf && pf->dest_pf);
     447                 :       25552 :             src_pe = list_nth(graph_path, pf->src_pf->factorpos);
     448                 :       25552 :             dest_pe = list_nth(graph_path, pf->dest_pf->factorpos);
     449                 :             : 
     450                 :             :             /* Make sure that the links of adjacent vertices are correct. */
     451                 :             :             Assert(pf->src_pf == src_pe->path_factor &&
     452                 :             :                    pf->dest_pf == dest_pe->path_factor);
     453                 :             : 
     454         [ +  + ]:       25552 :             if (src_pe->elemoid == pe->srcvertexid &&
     455         [ +  + ]:        8824 :                 dest_pe->elemoid == pe->destvertexid)
     456                 :        3100 :                 edge_qual = makeBoolExpr(AND_EXPR,
     457                 :        3100 :                                          list_concat(copyObject(pe->src_quals),
     458                 :        3100 :                                                      copyObject(pe->dest_quals)),
     459                 :             :                                          -1);
     460                 :             : 
     461                 :             :             /*
     462                 :             :              * An edge pattern in any direction matches edges in both
     463                 :             :              * directions, try swapping source and destination. When the
     464                 :             :              * source and destination is the same vertex table, quals
     465                 :             :              * corresponding to either direction may get satisfied. Hence OR
     466                 :             :              * the quals corresponding to both the directions.
     467                 :             :              */
     468         [ +  + ]:       25552 :             if (pf->kind == EDGE_PATTERN_ANY &&
     469         [ +  + ]:         224 :                 dest_pe->elemoid == pe->srcvertexid &&
     470         [ +  + ]:          80 :                 src_pe->elemoid == pe->destvertexid)
     471                 :             :             {
     472                 :          24 :                 List       *src_quals = copyObject(pe->dest_quals);
     473                 :          24 :                 List       *dest_quals = copyObject(pe->src_quals);
     474                 :             :                 Expr       *rev_edge_qual;
     475                 :             : 
     476                 :             :                 /* Swap the source and destination varnos in the quals. */
     477                 :          24 :                 ChangeVarNodes((Node *) dest_quals, pe->path_factor->src_pf->factorpos + 1,
     478                 :          24 :                                pe->path_factor->dest_pf->factorpos + 1, 0);
     479                 :          24 :                 ChangeVarNodes((Node *) src_quals, pe->path_factor->dest_pf->factorpos + 1,
     480                 :          24 :                                pe->path_factor->src_pf->factorpos + 1, 0);
     481                 :             : 
     482                 :          24 :                 rev_edge_qual = makeBoolExpr(AND_EXPR, list_concat(src_quals, dest_quals), -1);
     483         [ +  + ]:          24 :                 if (edge_qual)
     484                 :          16 :                     edge_qual = makeBoolExpr(OR_EXPR, list_make2(edge_qual, rev_edge_qual), -1);
     485                 :             :                 else
     486                 :           8 :                     edge_qual = rev_edge_qual;
     487                 :             :             }
     488                 :             : 
     489                 :             :             /*
     490                 :             :              * If the given edge element does not connect the adjacent vertex
     491                 :             :              * elements in this path, the path is broken. Abandon this path as
     492                 :             :              * it won't return any rows.
     493                 :             :              */
     494         [ +  + ]:       25552 :             if (edge_qual == NULL)
     495                 :       22444 :                 return NULL;
     496                 :             : 
     497                 :        3108 :             qual_exprs = lappend(qual_exprs, edge_qual);
     498                 :             :         }
     499                 :             :         else
     500                 :             :             Assert(!pe->src_quals && !pe->dest_quals);
     501                 :             : 
     502                 :             :         /*
     503                 :             :          * Create RangeTblEntry for this element table.
     504                 :             :          *
     505                 :             :          * SQL/PGQ standard (Ref. Section 11.19, Access rule 2 and General
     506                 :             :          * rule 4) does not specify whose access privileges to use when
     507                 :             :          * accessing the element tables: property graph owner's or current
     508                 :             :          * user's. It is safer to use current user's privileges to avoid
     509                 :             :          * unprivileged data access through a property graph. This is inline
     510                 :             :          * with the views being security_invoker by default.
     511                 :             :          */
     512                 :       29412 :         rel = table_open(pe->reloid, AccessShareLock);
     513                 :       29412 :         pni = addRangeTableEntryForRelation(make_parsestate(NULL), rel, AccessShareLock,
     514                 :             :                                             NULL, true, false);
     515                 :       29412 :         table_close(rel, NoLock);
     516                 :       29412 :         path_query->rtable = lappend(path_query->rtable, pni->p_rte);
     517                 :       29412 :         path_query->rteperminfos = lappend(path_query->rteperminfos, pni->p_perminfo);
     518                 :       29412 :         pni->p_rte->perminfoindex = list_length(path_query->rteperminfos);
     519                 :       29412 :         rtr = makeNode(RangeTblRef);
     520                 :       29412 :         rtr->rtindex = list_length(path_query->rtable);
     521                 :       29412 :         fromlist = lappend(fromlist, rtr);
     522                 :             : 
     523                 :             :         /*
     524                 :             :          * Make sure that the assumption mentioned in create_pe_for_element()
     525                 :             :          * holds true; that the elements' RangeTblEntrys are added in the
     526                 :             :          * order in which their respective path factors appear in the list of
     527                 :             :          * path factors representing the path pattern.
     528                 :             :          */
     529                 :             :         Assert(pf->factorpos + 1 == rtr->rtindex);
     530                 :             : 
     531         [ +  + ]:       29412 :         if (pf->whereClause)
     532                 :             :         {
     533                 :             :             Node       *tr;
     534                 :             : 
     535                 :        3752 :             tr = replace_property_refs(rte->relid, pf->whereClause, list_make1(pe));
     536                 :             : 
     537                 :        3752 :             qual_exprs = lappend(qual_exprs, tr);
     538                 :             :         }
     539                 :             :     }
     540                 :             : 
     541         [ +  + ]:         876 :     if (rte->graph_pattern->whereClause)
     542                 :             :     {
     543                 :         350 :         Node       *path_quals = replace_property_refs(rte->relid,
     544                 :         350 :                                                        (Node *) rte->graph_pattern->whereClause,
     545                 :             :                                                        graph_path);
     546                 :             : 
     547                 :         350 :         qual_exprs = lappend(qual_exprs, path_quals);
     548                 :             :     }
     549                 :             : 
     550         [ +  + ]:        1640 :     path_query->jointree = makeFromExpr(fromlist,
     551                 :         764 :                                         qual_exprs ? (Node *) makeBoolExpr(AND_EXPR, qual_exprs, -1) : NULL);
     552                 :             : 
     553                 :             :     /* Construct query targetlist from COLUMNS specification of GRAPH_TABLE. */
     554                 :         876 :     path_query->targetList = castNode(List,
     555                 :             :                                       replace_property_refs(rte->relid,
     556                 :             :                                                             (Node *) rte->graph_table_columns,
     557                 :             :                                                             graph_path));
     558                 :             : 
     559                 :             :     /*
     560                 :             :      * Mark the columns being accessed in the path query as requiring SELECT
     561                 :             :      * privilege. Any lateral columns should have been handled when the
     562                 :             :      * corresponding ColumnRefs were transformed. Ignore those here.
     563                 :             :      */
     564                 :         856 :     vars = pull_vars_of_level((Node *) list_make2(qual_exprs, path_query->targetList), 0);
     565   [ +  +  +  +  :        9958 :     foreach_node(Var, var, vars)
                   +  + ]
     566                 :             :     {
     567                 :        8246 :         RTEPermissionInfo *perminfo = getRTEPermissionInfo(path_query->rteperminfos,
     568                 :        8246 :                                                            rt_fetch(var->varno, path_query->rtable));
     569                 :             : 
     570                 :             :         /* Must offset the attnum to fit in a bitmapset */
     571                 :        8246 :         perminfo->selectedCols = bms_add_member(perminfo->selectedCols,
     572                 :        8246 :                                                 var->varattno - FirstLowInvalidHeapAttributeNumber);
     573                 :             :     }
     574                 :             : 
     575                 :         856 :     return path_query;
     576                 :             : }
     577                 :             : 
     578                 :             : /*
     579                 :             :  * Construct a query which would not return any rows.
     580                 :             :  *
     581                 :             :  * More details in the prologue of generate_queries_for_path_pattern().
     582                 :             :  */
     583                 :             : static Query *
     584                 :           4 : generate_query_for_empty_path_pattern(RangeTblEntry *rte)
     585                 :             : {
     586                 :           4 :     Query      *query = makeNode(Query);
     587                 :             : 
     588                 :           4 :     query->commandType = CMD_SELECT;
     589                 :           4 :     query->rtable = NIL;
     590                 :           4 :     query->rteperminfos = NIL;
     591                 :           4 :     query->jointree = makeFromExpr(NIL, (Node *) makeBoolConst(false, false));
     592                 :             : 
     593                 :             :     /*
     594                 :             :      * Even though no rows are returned, the result still projects the same
     595                 :             :      * columns as projected by GRAPH_TABLE clause. Do this by constructing a
     596                 :             :      * target list full of NULL values.
     597                 :             :      */
     598   [ +  -  +  +  :          16 :     foreach_node(TargetEntry, te, rte->graph_table_columns)
                   +  + ]
     599                 :             :     {
     600                 :           8 :         Node       *nte = (Node *) te->expr;
     601                 :             : 
     602                 :           8 :         te->expr = (Expr *) makeNullConst(exprType(nte), exprTypmod(nte), exprCollation(nte));
     603                 :           8 :         query->targetList = lappend(query->targetList, te);
     604                 :             :     }
     605                 :             : 
     606                 :           4 :     return query;
     607                 :             : }
     608                 :             : 
     609                 :             : /*
     610                 :             :  * Construct a query which is UNION of given path queries.
     611                 :             :  *
     612                 :             :  * The UNION query derives collations of its targetlist entries from the
     613                 :             :  * corresponding targetlist entries of the path queries. The targetlists of path
     614                 :             :  * queries being UNION'ed already have collations assigned.  No separate
     615                 :             :  * collation assignment required in this function.
     616                 :             :  *
     617                 :             :  * The function destroys given pathqueries list while constructing
     618                 :             :  * SetOperationStmt recursively. Hence the function always returns with
     619                 :             :  * `pathqueries` set to NIL.
     620                 :             :  */
     621                 :             : static Query *
     622                 :         473 : generate_union_from_pathqueries(List **pathqueries)
     623                 :             : {
     624                 :         473 :     List       *rtable = NIL;
     625                 :         473 :     Query      *sampleQuery = linitial_node(Query, *pathqueries);
     626                 :             :     SetOperationStmt *sostmt;
     627                 :             :     Query      *union_query;
     628                 :             :     int         resno;
     629                 :             :     ListCell   *lctl,
     630                 :             :                *lct,
     631                 :             :                *lcm,
     632                 :             :                *lcc;
     633                 :             : 
     634                 :             :     Assert(list_length(*pathqueries) > 0);
     635                 :             : 
     636                 :             :     /* If there's only one pathquery, no need to construct a UNION query. */
     637         [ +  + ]:         473 :     if (list_length(*pathqueries) == 1)
     638                 :             :     {
     639                 :         342 :         *pathqueries = NIL;
     640                 :         342 :         return sampleQuery;
     641                 :             :     }
     642                 :             : 
     643                 :         131 :     sostmt = castNode(SetOperationStmt,
     644                 :             :                       generate_setop_from_pathqueries(*pathqueries, &rtable, NULL));
     645                 :             : 
     646                 :             :     /* Encapsulate the set operation statement into a Query. */
     647                 :         131 :     union_query = makeNode(Query);
     648                 :         131 :     union_query->commandType = CMD_SELECT;
     649                 :         131 :     union_query->rtable = rtable;
     650                 :         131 :     union_query->setOperations = (Node *) sostmt;
     651                 :         131 :     union_query->rteperminfos = NIL;
     652                 :         131 :     union_query->jointree = makeFromExpr(NIL, NULL);
     653                 :             : 
     654                 :             :     /*
     655                 :             :      * Generate dummy targetlist for outer query using column names from one
     656                 :             :      * of the queries and common datatypes/collations of topmost set
     657                 :             :      * operation.  It shouldn't matter which query. Also it shouldn't matter
     658                 :             :      * which RT index is used as varno in the target list entries, as long as
     659                 :             :      * it corresponds to a real RT entry; else funny things may happen when
     660                 :             :      * the tree is mashed by rule rewriting. So we use 1 since there's always
     661                 :             :      * one RT entry at least.
     662                 :             :      */
     663                 :             :     Assert(rt_fetch(1, rtable));
     664                 :         131 :     union_query->targetList = NULL;
     665                 :         131 :     resno = 1;
     666   [ +  -  +  +  :         480 :     forfour(lct, sostmt->colTypes,
          +  -  +  +  +  
          -  +  +  +  -  
          +  +  +  +  +  
          -  +  -  +  -  
                   +  + ]
     667                 :             :             lcm, sostmt->colTypmods,
     668                 :             :             lcc, sostmt->colCollations,
     669                 :             :             lctl, sampleQuery->targetList)
     670                 :             :     {
     671                 :         349 :         Oid         colType = lfirst_oid(lct);
     672                 :         349 :         int32       colTypmod = lfirst_int(lcm);
     673                 :         349 :         Oid         colCollation = lfirst_oid(lcc);
     674                 :         349 :         TargetEntry *sample_tle = (TargetEntry *) lfirst(lctl);
     675                 :             :         char       *colName;
     676                 :             :         TargetEntry *tle;
     677                 :             :         Var        *var;
     678                 :             : 
     679                 :             :         Assert(!sample_tle->resjunk);
     680                 :         349 :         colName = pstrdup(sample_tle->resname);
     681                 :         349 :         var = makeVar(1, sample_tle->resno, colType, colTypmod, colCollation, 0);
     682                 :         349 :         var->location = exprLocation((Node *) sample_tle->expr);
     683                 :         349 :         tle = makeTargetEntry((Expr *) var, (AttrNumber) resno++, colName, false);
     684                 :         349 :         union_query->targetList = lappend(union_query->targetList, tle);
     685                 :             :     }
     686                 :             : 
     687                 :         131 :     *pathqueries = NIL;
     688                 :         131 :     return union_query;
     689                 :             : }
     690                 :             : 
     691                 :             : /*
     692                 :             :  * Construct a query which is UNION of all the given path queries.
     693                 :             :  *
     694                 :             :  * The function destroys given pathqueries list while constructing
     695                 :             :  * SetOperationStmt recursively.
     696                 :             :  */
     697                 :             : static Node *
     698                 :         649 : generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist)
     699                 :             : {
     700                 :             :     SetOperationStmt *sostmt;
     701                 :             :     Query      *lquery;
     702                 :             :     Node       *rarg;
     703                 :         649 :     RangeTblRef *lrtr = makeNode(RangeTblRef);
     704                 :             :     List       *rtargetlist;
     705                 :             :     ParseNamespaceItem *pni;
     706                 :             : 
     707                 :             :     /* Guard against stack overflow due to many path queries. */
     708                 :         649 :     check_stack_depth();
     709                 :             : 
     710                 :             :     /* Recursion termination condition. */
     711         [ +  + ]:         649 :     if (list_length(pathqueries) == 0)
     712                 :             :     {
     713                 :         131 :         *targetlist = NIL;
     714                 :         131 :         return NULL;
     715                 :             :     }
     716                 :             : 
     717                 :         518 :     lquery = linitial_node(Query, pathqueries);
     718                 :             : 
     719                 :             :     /*
     720                 :             :      * Each path query will become a subquery of the UNION statement. So any
     721                 :             :      * Vars that already refer outside the path query must be adjusted for
     722                 :             :      * additional query level.
     723                 :             :      */
     724                 :         518 :     IncrementVarSublevelsUp((Node *) lquery, 1, 1);
     725                 :             : 
     726                 :         518 :     pni = addRangeTableEntryForSubquery(make_parsestate(NULL), lquery, NULL,
     727                 :             :                                         false, false);
     728                 :         518 :     *rtable = lappend(*rtable, pni->p_rte);
     729                 :         518 :     lrtr->rtindex = list_length(*rtable);
     730                 :         518 :     rarg = generate_setop_from_pathqueries(list_delete_first(pathqueries), rtable, &rtargetlist);
     731         [ +  + ]:         518 :     if (rarg == NULL)
     732                 :             :     {
     733                 :             :         /*
     734                 :             :          * No further path queries in the list. Convert the last query into a
     735                 :             :          * RangeTblRef as expected by SetOperationStmt. Extract a list of the
     736                 :             :          * non-junk TLEs for upper-level processing.
     737                 :             :          */
     738         [ +  - ]:         131 :         if (targetlist)
     739                 :             :         {
     740                 :         131 :             *targetlist = NIL;
     741   [ +  -  +  +  :         611 :             foreach_node(TargetEntry, tle, lquery->targetList)
                   +  + ]
     742                 :             :             {
     743         [ +  - ]:         349 :                 if (!tle->resjunk)
     744                 :         349 :                     *targetlist = lappend(*targetlist, tle);
     745                 :             :             }
     746                 :             :         }
     747                 :         131 :         return (Node *) lrtr;
     748                 :             :     }
     749                 :             : 
     750                 :         387 :     sostmt = makeNode(SetOperationStmt);
     751                 :         387 :     sostmt->op = SETOP_UNION;
     752                 :         387 :     sostmt->all = true;
     753                 :         387 :     sostmt->larg = (Node *) lrtr;
     754                 :         387 :     sostmt->rarg = rarg;
     755                 :         387 :     constructSetOpTargetlist(NULL, sostmt, lquery->targetList, rtargetlist, targetlist, "UNION", false);
     756                 :             : 
     757                 :         387 :     return (Node *) sostmt;
     758                 :             : }
     759                 :             : 
     760                 :             : /*
     761                 :             :  * Construct a path_element object for the graph element given by `elemoid`
     762                 :             :  * satisfied by the path factor `pf`.
     763                 :             :  *
     764                 :             :  * If the type of graph element does not fit the element pattern kind, the
     765                 :             :  * function returns NULL.
     766                 :             :  */
     767                 :             : static struct path_element *
     768                 :        4650 : create_pe_for_element(struct path_factor *pf, Oid elemoid)
     769                 :             : {
     770                 :        4650 :     HeapTuple   eletup = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(elemoid));
     771                 :             :     Form_pg_propgraph_element pgeform;
     772                 :             :     struct path_element *pe;
     773                 :             : 
     774         [ -  + ]:        4650 :     if (!eletup)
     775         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for property graph element %u", elemoid);
     776                 :        4650 :     pgeform = ((Form_pg_propgraph_element) GETSTRUCT(eletup));
     777                 :             : 
     778   [ +  +  +  + ]:        4650 :     if ((pgeform->pgekind == PGEKIND_VERTEX && pf->kind != VERTEX_PATTERN) ||
     779   [ +  +  +  +  :        4034 :         (pgeform->pgekind == PGEKIND_EDGE && !IS_EDGE_PATTERN(pf->kind)))
             +  +  +  + ]
     780                 :             :     {
     781                 :        1924 :         ReleaseSysCache(eletup);
     782                 :        1924 :         return NULL;
     783                 :             :     }
     784                 :             : 
     785                 :        2726 :     pe = palloc0_object(struct path_element);
     786                 :        2726 :     pe->path_factor = pf;
     787                 :        2726 :     pe->elemoid = elemoid;
     788                 :        2726 :     pe->reloid = pgeform->pgerelid;
     789                 :             : 
     790                 :             :     /*
     791                 :             :      * When a path is converted into a query
     792                 :             :      * (generate_query_for_graph_path()), a RangeTblEntry will be created for
     793                 :             :      * every element in the path.  Fixing rtindexes of RangeTblEntrys here
     794                 :             :      * makes it possible to craft elements' qual expressions only once while
     795                 :             :      * we have access to the catalog entry. Otherwise they need to be crafted
     796                 :             :      * as many times as the number of paths a given element appears in,
     797                 :             :      * fetching catalog entry again each time.  Hence we simply assume
     798                 :             :      * RangeTblEntrys will be created in the same order in which the
     799                 :             :      * corresponding path factors appear in the list of path factors
     800                 :             :      * representing a path pattern. That way their rtindexes will be same as
     801                 :             :      * path_factor::factorpos + 1.
     802                 :             :      */
     803   [ +  +  +  +  :        2726 :     if (IS_EDGE_PATTERN(pf->kind))
                   +  + ]
     804                 :             :     {
     805                 :        1252 :         pe->srcvertexid = pgeform->pgesrcvertexid;
     806                 :        1252 :         pe->destvertexid = pgeform->pgedestvertexid;
     807                 :             :         Assert(pf->src_pf && pf->dest_pf);
     808                 :             : 
     809                 :        1252 :         pe->src_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->src_pf->factorpos + 1,
     810                 :             :                                                      pe->srcvertexid,
     811                 :             :                                                      Anum_pg_propgraph_element_pgesrckey,
     812                 :             :                                                      Anum_pg_propgraph_element_pgesrcref,
     813                 :             :                                                      Anum_pg_propgraph_element_pgesrceqop);
     814                 :        1252 :         pe->dest_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->dest_pf->factorpos + 1,
     815                 :             :                                                       pe->destvertexid,
     816                 :             :                                                       Anum_pg_propgraph_element_pgedestkey,
     817                 :             :                                                       Anum_pg_propgraph_element_pgedestref,
     818                 :             :                                                       Anum_pg_propgraph_element_pgedesteqop);
     819                 :             :     }
     820                 :             : 
     821                 :        2726 :     ReleaseSysCache(eletup);
     822                 :             : 
     823                 :        2726 :     return pe;
     824                 :             : }
     825                 :             : 
     826                 :             : /*
     827                 :             :  * Returns the list of OIDs of graph labels which the given label expression
     828                 :             :  * resolves to in the given property graph.
     829                 :             :  */
     830                 :             : static List *
     831                 :        1361 : get_labels_for_expr(Oid propgraphid, Node *labelexpr)
     832                 :             : {
     833                 :             :     List       *label_oids;
     834                 :             : 
     835         [ +  + ]:        1361 :     if (!labelexpr)
     836                 :             :     {
     837                 :             :         Relation    rel;
     838                 :             :         SysScanDesc scan;
     839                 :             :         ScanKeyData key[1];
     840                 :             :         HeapTuple   tup;
     841                 :             : 
     842                 :             :         /*
     843                 :             :          * According to section 9.2 "Contextual inference of a set of labels"
     844                 :             :          * subclause 2.a.ii of SQL/PGQ standard, element pattern which does
     845                 :             :          * not have a label expression is considered to have label expression
     846                 :             :          * equivalent to '%|!%' which is set of all labels.
     847                 :             :          */
     848                 :         408 :         label_oids = NIL;
     849                 :         408 :         rel = table_open(PropgraphLabelRelationId, AccessShareLock);
     850                 :         408 :         ScanKeyInit(&key[0],
     851                 :             :                     Anum_pg_propgraph_label_pglpgid,
     852                 :             :                     BTEqualStrategyNumber,
     853                 :             :                     F_OIDEQ, ObjectIdGetDatum(propgraphid));
     854                 :         408 :         scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId,
     855                 :             :                                   true, NULL, 1, key);
     856         [ +  + ]:        2728 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     857                 :             :         {
     858                 :        2320 :             Form_pg_propgraph_label label = (Form_pg_propgraph_label) GETSTRUCT(tup);
     859                 :             : 
     860                 :        2320 :             label_oids = lappend_oid(label_oids, label->oid);
     861                 :             :         }
     862                 :         408 :         systable_endscan(scan);
     863                 :         408 :         table_close(rel, AccessShareLock);
     864                 :             :     }
     865         [ +  + ]:         953 :     else if (IsA(labelexpr, GraphLabelRef))
     866                 :             :     {
     867                 :         907 :         GraphLabelRef *glr = castNode(GraphLabelRef, labelexpr);
     868                 :             : 
     869                 :         907 :         label_oids = list_make1_oid(glr->labelid);
     870                 :             :     }
     871         [ +  - ]:          46 :     else if (IsA(labelexpr, BoolExpr))
     872                 :             :     {
     873                 :          46 :         BoolExpr   *be = castNode(BoolExpr, labelexpr);
     874                 :          46 :         List       *label_exprs = be->args;
     875                 :             : 
     876                 :          46 :         label_oids = NIL;
     877   [ +  -  +  +  :         188 :         foreach_node(GraphLabelRef, glr, label_exprs)
                   +  + ]
     878                 :          96 :             label_oids = lappend_oid(label_oids, glr->labelid);
     879                 :             :     }
     880                 :             :     else
     881                 :             :     {
     882                 :             :         /*
     883                 :             :          * should not reach here since gram.y will not generate a label
     884                 :             :          * expression with other node types.
     885                 :             :          */
     886         [ #  # ]:           0 :         elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
     887                 :             :     }
     888                 :             : 
     889                 :        1361 :     return label_oids;
     890                 :             : }
     891                 :             : 
     892                 :             : /*
     893                 :             :  * Return a list of all the graph elements that satisfy the graph element pattern
     894                 :             :  * represented by the given path_factor `pf`.
     895                 :             :  *
     896                 :             :  * First we find all the graph labels that satisfy the label expression in path
     897                 :             :  * factor. Each label is associated with one or more graph elements.  A union of
     898                 :             :  * all such elements satisfies the element pattern. We create one path_element
     899                 :             :  * object representing every element whose graph element kind qualifies the
     900                 :             :  * element pattern kind. A list of all such path_element objects is returned.
     901                 :             :  *
     902                 :             :  * Note that we need to report an error for an explicitly specified label which
     903                 :             :  * is not associated with any graph element of the required kind. So we have to
     904                 :             :  * treat each label separately. Without that requirement we could have collected
     905                 :             :  * all the unique elements first and then created path_element objects for them
     906                 :             :  * to simplify the code.
     907                 :             :  */
     908                 :             : static List *
     909                 :        1361 : get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf)
     910                 :             : {
     911                 :        1361 :     List       *label_oids = get_labels_for_expr(propgraphid, pf->labelexpr);
     912                 :        1361 :     List       *elem_oids_seen = NIL;
     913                 :        1361 :     List       *pf_elem_oids = NIL;
     914                 :        1361 :     List       *path_elements = NIL;
     915                 :        1361 :     List       *unresolved_labels = NIL;
     916                 :             :     Relation    rel;
     917                 :             :     SysScanDesc scan;
     918                 :             :     ScanKeyData key[1];
     919                 :             :     HeapTuple   tup;
     920                 :             : 
     921                 :             :     /*
     922                 :             :      * A property graph element can be either a vertex or an edge. Other types
     923                 :             :      * of path factors like nested path pattern need to be handled separately
     924                 :             :      * when supported.
     925                 :             :      */
     926                 :             :     Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
     927                 :             : 
     928                 :        1361 :     rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
     929   [ +  -  +  +  :        6025 :     foreach_oid(labeloid, label_oids)
                   +  + ]
     930                 :             :     {
     931                 :        3319 :         bool        found = false;
     932                 :             : 
     933                 :        3319 :         ScanKeyInit(&key[0],
     934                 :             :                     Anum_pg_propgraph_element_label_pgellabelid,
     935                 :             :                     BTEqualStrategyNumber,
     936                 :             :                     F_OIDEQ, ObjectIdGetDatum(labeloid));
     937                 :        3319 :         scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true,
     938                 :             :                                   NULL, 1, key);
     939         [ +  + ]:       11047 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     940                 :             :         {
     941                 :        7728 :             Form_pg_propgraph_element_label label_elem = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
     942                 :        7728 :             Oid         elem_oid = label_elem->pgelelid;
     943                 :             : 
     944         [ +  + ]:        7728 :             if (!list_member_oid(elem_oids_seen, elem_oid))
     945                 :             :             {
     946                 :             :                 /*
     947                 :             :                  * Create path_element object if the new element qualifies the
     948                 :             :                  * element pattern kind.
     949                 :             :                  */
     950                 :        4650 :                 struct path_element *pe = create_pe_for_element(pf, elem_oid);
     951                 :             : 
     952         [ +  + ]:        4650 :                 if (pe)
     953                 :             :                 {
     954                 :        2726 :                     path_elements = lappend(path_elements, pe);
     955                 :             : 
     956                 :             :                     /* Remember qualified elements. */
     957                 :        2726 :                     pf_elem_oids = lappend_oid(pf_elem_oids, elem_oid);
     958                 :        2726 :                     found = true;
     959                 :             :                 }
     960                 :             : 
     961                 :             :                 /*
     962                 :             :                  * Remember qualified and unqualified elements processed so
     963                 :             :                  * far to avoid processing already processed elements again.
     964                 :             :                  */
     965                 :        4650 :                 elem_oids_seen = lappend_oid(elem_oids_seen, label_elem->pgelelid);
     966                 :             :             }
     967         [ +  + ]:        3078 :             else if (list_member_oid(pf_elem_oids, elem_oid))
     968                 :             :             {
     969                 :             :                 /*
     970                 :             :                  * The graph element is known to qualify the given element
     971                 :             :                  * pattern. Flag that the current label has at least one
     972                 :             :                  * qualified element associated with it.
     973                 :             :                  */
     974                 :        1582 :                 found = true;
     975                 :             :             }
     976                 :             :         }
     977                 :             : 
     978         [ +  + ]:        3319 :         if (!found)
     979                 :             :         {
     980                 :             :             /*
     981                 :             :              * We did not find any qualified element associated with this
     982                 :             :              * label. The label or its properties can not be associated with
     983                 :             :              * the given element pattern. Throw an error if the label was
     984                 :             :              * explicitly specified in the element pattern. Otherwise remember
     985                 :             :              * it for later use.
     986                 :             :              */
     987         [ +  + ]:         984 :             if (!pf->labelexpr)
     988                 :         976 :                 unresolved_labels = lappend_oid(unresolved_labels, labeloid);
     989                 :             :             else
     990   [ +  -  +  - ]:           8 :                 ereport(ERROR,
     991                 :             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     992                 :             :                          errmsg("no property graph element of type \"%s\" has label \"%s\" associated with it in property graph \"%s\"",
     993                 :             :                                 pf->kind == VERTEX_PATTERN ? "vertex" : "edge",
     994                 :             :                                 get_propgraph_label_name(labeloid),
     995                 :             :                                 get_rel_name(propgraphid))));
     996                 :             :         }
     997                 :             : 
     998                 :        3311 :         systable_endscan(scan);
     999                 :             :     }
    1000                 :        1353 :     table_close(rel, AccessShareLock);
    1001                 :             : 
    1002                 :             :     /*
    1003                 :             :      * Remove the labels which were not explicitly mentioned in the label
    1004                 :             :      * expression but do not have any qualified elements associated with them.
    1005                 :             :      * Properties associated with such labels may not be referenced. See
    1006                 :             :      * replace_property_refs_mutator() for more details.
    1007                 :             :      */
    1008                 :        1353 :     pf->labeloids = list_difference_oid(label_oids, unresolved_labels);
    1009                 :             : 
    1010                 :        1353 :     return path_elements;
    1011                 :             : }
    1012                 :             : 
    1013                 :             : /*
    1014                 :             :  * Mutating property references into table variables
    1015                 :             :  */
    1016                 :             : 
    1017                 :             : struct replace_property_refs_context
    1018                 :             : {
    1019                 :             :     Oid         propgraphid;
    1020                 :             :     const List *mappings;
    1021                 :             : };
    1022                 :             : 
    1023                 :             : static Node *
    1024                 :       50452 : replace_property_refs_mutator(Node *node, struct replace_property_refs_context *context)
    1025                 :             : {
    1026         [ -  + ]:       50452 :     if (node == NULL)
    1027                 :           0 :         return NULL;
    1028         [ +  + ]:       50452 :     if (IsA(node, Var))
    1029                 :             :     {
    1030                 :          56 :         Var        *var = (Var *) node;
    1031                 :          56 :         Var        *newvar = copyObject(var);
    1032                 :             : 
    1033                 :             :         /*
    1034                 :             :          * If it's already a Var, then it was a lateral reference.  Since we
    1035                 :             :          * are in a subquery after the rewrite, we have to increase the level
    1036                 :             :          * by one.
    1037                 :             :          */
    1038                 :          56 :         newvar->varlevelsup++;
    1039                 :             : 
    1040                 :          56 :         return (Node *) newvar;
    1041                 :             :     }
    1042         [ +  + ]:       50396 :     else if (IsA(node, GraphPropertyRef))
    1043                 :             :     {
    1044                 :       10994 :         GraphPropertyRef *gpr = (GraphPropertyRef *) node;
    1045                 :       10994 :         Node       *n = NULL;
    1046                 :       10994 :         struct path_element *found_mapping = NULL;
    1047                 :       10994 :         struct path_factor *mapping_factor = NULL;
    1048                 :       10994 :         List       *unrelated_labels = NIL;
    1049                 :             : 
    1050   [ +  -  +  -  :       25300 :         foreach_ptr(struct path_element, m, context->mappings)
                   +  + ]
    1051                 :             :         {
    1052   [ +  +  +  + ]:       14306 :             if (m->path_factor->variable && strcmp(gpr->elvarname, m->path_factor->variable) == 0)
    1053                 :             :             {
    1054                 :       10994 :                 found_mapping = m;
    1055                 :       10994 :                 break;
    1056                 :             :             }
    1057                 :             :         }
    1058                 :             : 
    1059                 :             :         /*
    1060                 :             :          * transformGraphTablePropertyRef() would not create a
    1061                 :             :          * GraphPropertyRef for a variable which is not present in the graph
    1062                 :             :          * path pattern.
    1063                 :             :          */
    1064                 :             :         Assert(found_mapping);
    1065                 :             : 
    1066                 :       10994 :         mapping_factor = found_mapping->path_factor;
    1067                 :             : 
    1068                 :             :         /*
    1069                 :             :          * Find property definition for given element through any of the
    1070                 :             :          * associated labels qualifying the given element pattern.
    1071                 :             :          */
    1072   [ +  -  +  +  :       60022 :         foreach_oid(labeloid, mapping_factor->labeloids)
                   +  + ]
    1073                 :             :         {
    1074                 :       38034 :             Oid         elem_labelid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
    1075                 :             :                                                        Anum_pg_propgraph_element_label_oid,
    1076                 :             :                                                        ObjectIdGetDatum(found_mapping->elemoid),
    1077                 :             :                                                        ObjectIdGetDatum(labeloid));
    1078                 :             : 
    1079         [ +  + ]:       38034 :             if (OidIsValid(elem_labelid))
    1080                 :             :             {
    1081                 :       23054 :                 HeapTuple   tup = SearchSysCache2(PROPGRAPHLABELPROP, ObjectIdGetDatum(elem_labelid),
    1082                 :             :                                                   ObjectIdGetDatum(gpr->propid));
    1083                 :             : 
    1084         [ +  + ]:       23054 :                 if (!tup)
    1085                 :             :                 {
    1086                 :             :                     /*
    1087                 :             :                      * The label is associated with the given element but it
    1088                 :             :                      * is not associated with the required property. Check
    1089                 :             :                      * next label.
    1090                 :             :                      */
    1091                 :       11720 :                     continue;
    1092                 :             :                 }
    1093                 :             : 
    1094                 :       11334 :                 n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHLABELPROP,
    1095                 :             :                                                                             tup, Anum_pg_propgraph_label_property_plpexpr)));
    1096                 :       11334 :                 ChangeVarNodes(n, 1, mapping_factor->factorpos + 1, 0);
    1097                 :             : 
    1098                 :       11334 :                 ReleaseSysCache(tup);
    1099                 :             :             }
    1100                 :             :             else
    1101                 :             :             {
    1102                 :             :                 /*
    1103                 :             :                  * Label is not associated with the element but it may be
    1104                 :             :                  * associated with the property through some other element.
    1105                 :             :                  * Save it for later use.
    1106                 :             :                  */
    1107                 :       14980 :                 unrelated_labels = lappend_oid(unrelated_labels, labeloid);
    1108                 :             :             }
    1109                 :             :         }
    1110                 :             : 
    1111                 :             :         /* See if we can resolve the property in some other way. */
    1112         [ +  + ]:       10994 :         if (!n)
    1113                 :             :         {
    1114                 :          72 :             bool        prop_associated = false;
    1115                 :             : 
    1116   [ +  +  +  +  :         168 :             foreach_oid(loid, unrelated_labels)
                   +  + ]
    1117                 :             :             {
    1118         [ +  + ]:          76 :                 if (is_property_associated_with_label(loid, gpr->propid))
    1119                 :             :                 {
    1120                 :          52 :                     prop_associated = true;
    1121                 :          52 :                     break;
    1122                 :             :                 }
    1123                 :             :             }
    1124                 :             : 
    1125         [ +  + ]:          72 :             if (prop_associated)
    1126                 :             :             {
    1127                 :             :                 /*
    1128                 :             :                  * The property is associated with at least one of the labels
    1129                 :             :                  * that satisfy given element pattern. If it's associated with
    1130                 :             :                  * the given element (through some other label), use
    1131                 :             :                  * corresponding value expression. Otherwise NULL. Ref.
    1132                 :             :                  * SQL/PGQ standard section 6.5 Property Reference, General
    1133                 :             :                  * Rule 2.b.
    1134                 :             :                  */
    1135                 :          52 :                 n = get_element_property_expr(found_mapping->elemoid, gpr->propid,
    1136                 :          52 :                                               mapping_factor->factorpos + 1);
    1137                 :             : 
    1138         [ +  + ]:          52 :                 if (!n)
    1139                 :          48 :                     n = (Node *) makeNullConst(gpr->typeId, gpr->typmod, gpr->collation);
    1140                 :             :             }
    1141                 :             : 
    1142                 :             :         }
    1143                 :             : 
    1144         [ +  + ]:       10994 :         if (!n)
    1145         [ +  - ]:          20 :             ereport(ERROR,
    1146                 :             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1147                 :             :                     errmsg("property \"%s\" for element variable \"%s\" not found",
    1148                 :             :                            get_propgraph_property_name(gpr->propid), mapping_factor->variable));
    1149                 :             : 
    1150                 :       10974 :         return n;
    1151                 :             :     }
    1152                 :             : 
    1153                 :       39402 :     return expression_tree_mutator(node, replace_property_refs_mutator, context);
    1154                 :             : }
    1155                 :             : 
    1156                 :             : static Node *
    1157                 :        4978 : replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
    1158                 :             : {
    1159                 :             :     struct replace_property_refs_context context;
    1160                 :             : 
    1161                 :        4978 :     context.mappings = mappings;
    1162                 :        4978 :     context.propgraphid = propgraphid;
    1163                 :             : 
    1164                 :        4978 :     return replace_property_refs_mutator(node, &context);
    1165                 :             : }
    1166                 :             : 
    1167                 :             : /*
    1168                 :             :  * Build join qualification expressions between edge and vertex tables.
    1169                 :             :  */
    1170                 :             : static List *
    1171                 :        2504 : build_edge_vertex_link_quals(HeapTuple edgetup, int edgerti, int refrti, Oid refid, AttrNumber catalog_key_attnum, AttrNumber catalog_ref_attnum, AttrNumber catalog_eqop_attnum)
    1172                 :             : {
    1173                 :        2504 :     List       *quals = NIL;
    1174                 :             :     Form_pg_propgraph_element pgeform;
    1175                 :             :     Datum       datum;
    1176                 :             :     Datum      *d1,
    1177                 :             :                *d2,
    1178                 :             :                *d3;
    1179                 :             :     int         n1,
    1180                 :             :                 n2,
    1181                 :             :                 n3;
    1182                 :        2504 :     ParseState *pstate = make_parsestate(NULL);
    1183                 :        2504 :     Oid         refrelid = GetSysCacheOid1(PROPGRAPHELOID, Anum_pg_propgraph_element_pgerelid, ObjectIdGetDatum(refid));
    1184                 :             : 
    1185                 :        2504 :     pgeform = (Form_pg_propgraph_element) GETSTRUCT(edgetup);
    1186                 :             : 
    1187                 :        2504 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_key_attnum);
    1188                 :        2504 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d1, NULL, &n1);
    1189                 :             : 
    1190                 :        2504 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_ref_attnum);
    1191                 :        2504 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d2, NULL, &n2);
    1192                 :             : 
    1193                 :        2504 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_eqop_attnum);
    1194                 :        2504 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &d3, NULL, &n3);
    1195                 :             : 
    1196         [ -  + ]:        2504 :     if (n1 != n2)
    1197         [ #  # ]:           0 :         elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
    1198         [ -  + ]:        2504 :     if (n1 != n3)
    1199         [ #  # ]:           0 :         elog(ERROR, "array size key (%d) vs operator (%d) mismatch for element ID %u", catalog_key_attnum, catalog_eqop_attnum, pgeform->oid);
    1200                 :             : 
    1201         [ +  + ]:        5672 :     for (int i = 0; i < n1; i++)
    1202                 :             :     {
    1203                 :        3168 :         AttrNumber  keyattn = DatumGetInt16(d1[i]);
    1204                 :        3168 :         AttrNumber  refattn = DatumGetInt16(d2[i]);
    1205                 :        3168 :         Oid         eqop = DatumGetObjectId(d3[i]);
    1206                 :             :         Var        *keyvar;
    1207                 :             :         Var        *refvar;
    1208                 :             :         Oid         atttypid;
    1209                 :             :         int32       atttypmod;
    1210                 :             :         Oid         attcoll;
    1211                 :             :         HeapTuple   tup;
    1212                 :             :         Form_pg_operator opform;
    1213                 :             :         List       *args;
    1214                 :             :         Oid         actual_arg_types[2];
    1215                 :             :         Oid         declared_arg_types[2];
    1216                 :             :         OpExpr     *linkqual;
    1217                 :             : 
    1218                 :        3168 :         get_atttypetypmodcoll(pgeform->pgerelid, keyattn, &atttypid, &atttypmod, &attcoll);
    1219                 :        3168 :         keyvar = makeVar(edgerti, keyattn, atttypid, atttypmod, attcoll, 0);
    1220                 :        3168 :         get_atttypetypmodcoll(refrelid, refattn, &atttypid, &atttypmod, &attcoll);
    1221                 :        3168 :         refvar = makeVar(refrti, refattn, atttypid, atttypmod, attcoll, 0);
    1222                 :             : 
    1223                 :        3168 :         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(eqop));
    1224         [ -  + ]:        3168 :         if (!HeapTupleIsValid(tup))
    1225         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for operator %u", eqop);
    1226                 :        3168 :         opform = (Form_pg_operator) GETSTRUCT(tup);
    1227                 :             :         /* An equality operator is a binary operator returning boolean result. */
    1228                 :             :         Assert(opform->oprkind == 'b'
    1229                 :             :                && RegProcedureIsValid(opform->oprcode)
    1230                 :             :                && opform->oprresult == BOOLOID
    1231                 :             :                && !get_func_retset(opform->oprcode));
    1232                 :             : 
    1233                 :             :         /*
    1234                 :             :          * Prepare operands and cast them to the types required by the
    1235                 :             :          * equality operator. Similar to PK/FK quals, referenced vertex key is
    1236                 :             :          * used as left operand and referencing edge key is used as right
    1237                 :             :          * operand.
    1238                 :             :          */
    1239                 :        3168 :         args = list_make2(refvar, keyvar);
    1240                 :        3168 :         actual_arg_types[0] = exprType((Node *) refvar);
    1241                 :        3168 :         actual_arg_types[1] = exprType((Node *) keyvar);
    1242                 :        3168 :         declared_arg_types[0] = opform->oprleft;
    1243                 :        3168 :         declared_arg_types[1] = opform->oprright;
    1244                 :        3168 :         make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
    1245                 :             : 
    1246                 :        3168 :         linkqual = makeNode(OpExpr);
    1247                 :        3168 :         linkqual->opno = opform->oid;
    1248                 :        3168 :         linkqual->opfuncid = opform->oprcode;
    1249                 :        3168 :         linkqual->opresulttype = opform->oprresult;
    1250                 :        3168 :         linkqual->opretset = false;
    1251                 :             :         /* opcollid and inputcollid will be set by parse_collate.c */
    1252                 :        3168 :         linkqual->args = args;
    1253                 :        3168 :         linkqual->location = -1;
    1254                 :             : 
    1255                 :        3168 :         ReleaseSysCache(tup);
    1256                 :        3168 :         quals = lappend(quals, linkqual);
    1257                 :             :     }
    1258                 :             : 
    1259                 :        2504 :     assign_expr_collations(pstate, (Node *) quals);
    1260                 :             : 
    1261                 :        2504 :     return quals;
    1262                 :             : }
    1263                 :             : 
    1264                 :             : /*
    1265                 :             :  * Check if the given property is associated with the given label.
    1266                 :             :  *
    1267                 :             :  * A label projects the same set of properties through every element it is
    1268                 :             :  * associated with. Find any of the elements and return true if that element is
    1269                 :             :  * associated with the given property. False otherwise.
    1270                 :             :  */
    1271                 :             : static bool
    1272                 :          76 : is_property_associated_with_label(Oid labeloid, Oid propoid)
    1273                 :             : {
    1274                 :             :     Relation    rel;
    1275                 :             :     SysScanDesc scan;
    1276                 :             :     ScanKeyData key[1];
    1277                 :             :     HeapTuple   tup;
    1278                 :          76 :     bool        associated = false;
    1279                 :             : 
    1280                 :          76 :     rel = table_open(PropgraphElementLabelRelationId, RowShareLock);
    1281                 :          76 :     ScanKeyInit(&key[0],
    1282                 :             :                 Anum_pg_propgraph_element_label_pgellabelid,
    1283                 :             :                 BTEqualStrategyNumber,
    1284                 :             :                 F_OIDEQ, ObjectIdGetDatum(labeloid));
    1285                 :          76 :     scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId,
    1286                 :             :                               true, NULL, 1, key);
    1287                 :             : 
    1288         [ +  - ]:          76 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
    1289                 :             :     {
    1290                 :          76 :         Form_pg_propgraph_element_label ele_label = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
    1291                 :             : 
    1292                 :          76 :         associated = SearchSysCacheExists2(PROPGRAPHLABELPROP,
    1293                 :             :                                            ObjectIdGetDatum(ele_label->oid), ObjectIdGetDatum(propoid));
    1294                 :             :     }
    1295                 :          76 :     systable_endscan(scan);
    1296                 :          76 :     table_close(rel, RowShareLock);
    1297                 :             : 
    1298                 :          76 :     return associated;
    1299                 :             : }
    1300                 :             : 
    1301                 :             : /*
    1302                 :             :  * If given element has the given property associated with it, through any of
    1303                 :             :  * the associated labels, return value expression of the property. Otherwise
    1304                 :             :  * NULL.
    1305                 :             :  */
    1306                 :             : static Node *
    1307                 :          52 : get_element_property_expr(Oid elemoid, Oid propoid, int rtindex)
    1308                 :             : {
    1309                 :             :     Relation    rel;
    1310                 :             :     SysScanDesc scan;
    1311                 :             :     ScanKeyData key[1];
    1312                 :             :     HeapTuple   labeltup;
    1313                 :          52 :     Node       *n = NULL;
    1314                 :             : 
    1315                 :          52 :     rel = table_open(PropgraphElementLabelRelationId, RowShareLock);
    1316                 :          52 :     ScanKeyInit(&key[0],
    1317                 :             :                 Anum_pg_propgraph_element_label_pgelelid,
    1318                 :             :                 BTEqualStrategyNumber,
    1319                 :             :                 F_OIDEQ, ObjectIdGetDatum(elemoid));
    1320                 :          52 :     scan = systable_beginscan(rel, PropgraphElementLabelElementLabelIndexId,
    1321                 :             :                               true, NULL, 1, key);
    1322                 :             : 
    1323         [ +  + ]:         148 :     while (HeapTupleIsValid(labeltup = systable_getnext(scan)))
    1324                 :             :     {
    1325                 :         100 :         Form_pg_propgraph_element_label ele_label = (Form_pg_propgraph_element_label) GETSTRUCT(labeltup);
    1326                 :             : 
    1327                 :         100 :         HeapTuple   proptup = SearchSysCache2(PROPGRAPHLABELPROP,
    1328                 :             :                                               ObjectIdGetDatum(ele_label->oid), ObjectIdGetDatum(propoid));
    1329                 :             : 
    1330         [ +  + ]:         100 :         if (!proptup)
    1331                 :          96 :             continue;
    1332                 :           4 :         n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHLABELPROP,
    1333                 :             :                                                                     proptup, Anum_pg_propgraph_label_property_plpexpr)));
    1334                 :           4 :         ChangeVarNodes(n, 1, rtindex, 0);
    1335                 :             : 
    1336                 :           4 :         ReleaseSysCache(proptup);
    1337                 :           4 :         break;
    1338                 :             :     }
    1339                 :          52 :     systable_endscan(scan);
    1340                 :          52 :     table_close(rel, RowShareLock);
    1341                 :             : 
    1342                 :          52 :     return n;
    1343                 :             : }
        

Generated by: LCOV version 2.0-1