LCOV - code coverage report
Current view: top level - src/backend/rewrite - rewriteGraphTable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 97.7 % 397 388
Test Date: 2026-07-25 22:15:46 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 81.1 % 302 245

             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                 :         514 : rewriteGraphTable(Query *parsetree, int rt_index)
     111                 :             : {
     112                 :             :     RangeTblEntry *rte;
     113                 :             :     Query      *graph_table_query;
     114                 :             :     List       *path_pattern;
     115                 :         514 :     List       *pathqueries = NIL;
     116                 :             : 
     117                 :         514 :     rte = rt_fetch(rt_index, parsetree->rtable);
     118                 :             : 
     119                 :             :     Assert(list_length(rte->graph_pattern->path_pattern_list) == 1);
     120                 :             : 
     121                 :         514 :     path_pattern = linitial(rte->graph_pattern->path_pattern_list);
     122                 :         514 :     pathqueries = generate_queries_for_path_pattern(rte, path_pattern);
     123                 :         474 :     graph_table_query = generate_union_from_pathqueries(&pathqueries);
     124                 :             : 
     125                 :         474 :     AcquireRewriteLocks(graph_table_query, true, false);
     126                 :             : 
     127                 :         474 :     rte->rtekind = RTE_SUBQUERY;
     128                 :         474 :     rte->subquery = graph_table_query;
     129                 :         474 :     rte->lateral = true;
     130                 :             : 
     131                 :             :     /*
     132                 :             :      * Reset no longer applicable fields, to appease
     133                 :             :      * WRITE_READ_PARSE_PLAN_TREES.
     134                 :             :      */
     135                 :         474 :     rte->graph_pattern = NULL;
     136                 :         474 :     rte->graph_table_columns = NIL;
     137                 :             : 
     138                 :         474 :     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                 :         514 : generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern)
     176                 :             : {
     177                 :         514 :     List       *pathqueries = NIL;
     178                 :         514 :     List       *path_elem_lists = NIL;
     179                 :         514 :     int         factorpos = 0;
     180                 :         514 :     List       *path_factors = NIL;
     181                 :         514 :     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   [ +  -  +  +  :        2568 :     foreach_node(GraphElementPattern, gep, path_pattern)
                   +  + ]
     193                 :             :     {
     194                 :        1564 :         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   [ +  +  +  +  :        4687 :         foreach_ptr(struct path_factor, other, path_factors)
                   +  + ]
     205                 :             :         {
     206   [ +  +  +  + ]:        1719 :             if (gep->variable && other->variable &&
     207         [ +  + ]:        1133 :                 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         [ +  + ]:        1556 :         if (!pf)
     249                 :             :         {
     250                 :        1412 :             pf = palloc0_object(struct path_factor);
     251                 :        1412 :             pf->factorpos = factorpos++;
     252                 :        1412 :             pf->kind = gep->kind;
     253                 :        1412 :             pf->labelexpr = gep->labelexpr;
     254                 :        1412 :             pf->variable = gep->variable;
     255                 :        1412 :             pf->whereClause = gep->whereClause;
     256                 :             : 
     257                 :        1412 :             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         [ +  + ]:        1556 :         if (prev_pf)
     285                 :             :         {
     286   [ +  +  +  + ]:        1042 :             if (prev_pf->kind == EDGE_PATTERN_RIGHT || prev_pf->kind == EDGE_PATTERN_ANY)
     287                 :             :             {
     288                 :             :                 Assert(!IS_EDGE_PATTERN(pf->kind));
     289   [ +  +  -  + ]:         509 :                 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                 :         509 :                 prev_pf->dest_pf = pf;
     294                 :             :             }
     295         [ +  + ]:         533 :             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   [ +  +  +  + ]:        1042 :             if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY)
     311                 :             :             {
     312                 :             :                 Assert(!IS_EDGE_PATTERN(prev_pf->kind));
     313   [ +  +  +  + ]:         517 :                 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                 :         513 :                 pf->src_pf = prev_pf;
     318                 :             :             }
     319         [ +  + ]:         525 :             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                 :        1552 :         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   [ +  -  +  +  :        2352 :     foreach_ptr(struct path_factor, pf, path_factors)
                   +  + ]
     343                 :        1356 :         path_elem_lists = lappend(path_elem_lists,
     344                 :        1364 :                                   get_path_elements_for_path_factor(rte->relid, pf));
     345                 :             : 
     346                 :         494 :     pathqueries = generate_queries_for_path_pattern_recurse(rte, pathqueries,
     347                 :             :                                                             NIL, path_elem_lists, 0);
     348         [ +  + ]:         474 :     if (!pathqueries)
     349                 :           4 :         pathqueries = list_make1(generate_query_for_empty_path_pattern(rte));
     350                 :             : 
     351                 :         474 :     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                 :        9906 : generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, List *cur_path, List *path_elem_lists, int elempos)
     362                 :             : {
     363                 :        9906 :     List       *path_elems = list_nth_node(List, path_elem_lists, elempos);
     364                 :             : 
     365                 :             :     /* Guard against stack overflow due to complex path patterns. */
     366                 :        9906 :     check_stack_depth();
     367                 :             : 
     368   [ +  -  +  +  :       51337 :     foreach_ptr(struct path_element, pe, path_elems)
                   +  + ]
     369                 :             :     {
     370                 :             :         /* Update current path being built with current element. */
     371                 :       31653 :         cur_path = lappend(cur_path, pe);
     372                 :             : 
     373                 :             :         /*
     374                 :             :          * If this is the last element in the path, generate query for the
     375                 :             :          * completed path. Else recurse processing the next element.
     376                 :             :          */
     377         [ +  + ]:       31653 :         if (list_length(path_elem_lists) == list_length(cur_path))
     378                 :             :         {
     379                 :       22241 :             Query      *pathquery = generate_query_for_graph_path(rte, cur_path);
     380                 :             : 
     381                 :             :             Assert(elempos == list_length(path_elem_lists) - 1);
     382         [ +  + ]:       22221 :             if (pathquery)
     383                 :         841 :                 pathqueries = lappend(pathqueries, pathquery);
     384                 :             :         }
     385                 :             :         else
     386                 :        9412 :             pathqueries = generate_queries_for_path_pattern_recurse(rte, pathqueries,
     387                 :             :                                                                     cur_path,
     388                 :             :                                                                     path_elem_lists,
     389                 :             :                                                                     elempos + 1);
     390                 :             :         /* Make way for the next element at the same position. */
     391                 :       31589 :         cur_path = list_delete_last(cur_path);
     392                 :             :     }
     393                 :             : 
     394                 :        9842 :     return pathqueries;
     395                 :             : }
     396                 :             : 
     397                 :             : /*
     398                 :             :  * Construct a query representing given graph path.
     399                 :             :  *
     400                 :             :  * The query contains:
     401                 :             :  *
     402                 :             :  * 1. targetlist corresponding to the COLUMNS clause of GRAPH_TABLE clause
     403                 :             :  *
     404                 :             :  * 2. quals corresponding to the WHERE clause of individual elements, WHERE
     405                 :             :  * clause in GRAPH_TABLE clause and quals representing edge-vertex links.
     406                 :             :  *
     407                 :             :  * 3. fromlist containing all elements in the path
     408                 :             :  *
     409                 :             :  * The collations of property expressions are obtained from the catalog. The
     410                 :             :  * collations of expressions in COLUMNS and WHERE clauses are assigned before
     411                 :             :  * rewriting the graph table.  The collations of the edge-vertex link quals are
     412                 :             :  * assigned when crafting those quals. Thus everything in the query that requires
     413                 :             :  * collation assignment has been taken care of already. No separate collation
     414                 :             :  * assignment is required in this function.
     415                 :             :  *
     416                 :             :  * More details in the prologue of generate_queries_for_path_pattern().
     417                 :             :  */
     418                 :             : static Query *
     419                 :       22241 : generate_query_for_graph_path(RangeTblEntry *rte, List *graph_path)
     420                 :             : {
     421                 :       22241 :     Query      *path_query = makeNode(Query);
     422                 :       22241 :     List       *fromlist = NIL;
     423                 :       22241 :     List       *qual_exprs = NIL;
     424                 :             :     List       *vars;
     425                 :             : 
     426                 :       22241 :     path_query->commandType = CMD_SELECT;
     427                 :             : 
     428   [ +  -  +  +  :       51165 :     foreach_ptr(struct path_element, pe, graph_path)
                   +  + ]
     429                 :             :     {
     430                 :       49443 :         struct path_factor *pf = pe->path_factor;
     431                 :             :         RangeTblRef *rtr;
     432                 :             :         Relation    rel;
     433                 :             :         ParseNamespaceItem *pni;
     434                 :             : 
     435                 :             :         Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
     436                 :             : 
     437                 :             :         /* Add conditions representing edge connections. */
     438   [ +  +  +  +  :       49443 :         if (IS_EDGE_PATTERN(pf->kind))
                   +  + ]
     439                 :             :         {
     440                 :             :             struct path_element *src_pe;
     441                 :             :             struct path_element *dest_pe;
     442                 :       24353 :             Expr       *edge_qual = NULL;
     443                 :             : 
     444                 :             :             Assert(pf->src_pf && pf->dest_pf);
     445                 :       24353 :             src_pe = list_nth(graph_path, pf->src_pf->factorpos);
     446                 :       24353 :             dest_pe = list_nth(graph_path, pf->dest_pf->factorpos);
     447                 :             : 
     448                 :             :             /* Make sure that the links of adjacent vertices are correct. */
     449                 :             :             Assert(pf->src_pf == src_pe->path_factor &&
     450                 :             :                    pf->dest_pf == dest_pe->path_factor);
     451                 :             : 
     452         [ +  + ]:       24353 :             if (src_pe->elemoid == pe->srcvertexid &&
     453         [ +  + ]:        8417 :                 dest_pe->elemoid == pe->destvertexid)
     454                 :        2965 :                 edge_qual = makeBoolExpr(AND_EXPR,
     455                 :        2965 :                                          list_concat(copyObject(pe->src_quals),
     456                 :        2965 :                                                      copyObject(pe->dest_quals)),
     457                 :             :                                          -1);
     458                 :             : 
     459                 :             :             /*
     460                 :             :              * An edge pattern in any direction matches edges in both
     461                 :             :              * directions, try swapping source and destination. When the
     462                 :             :              * source and destination is the same vertex table, quals
     463                 :             :              * corresponding to either direction may get satisfied. Hence OR
     464                 :             :              * the quals corresponding to both the directions.
     465                 :             :              */
     466         [ +  + ]:       24353 :             if (pf->kind == EDGE_PATTERN_ANY &&
     467         [ +  + ]:         224 :                 dest_pe->elemoid == pe->srcvertexid &&
     468         [ +  + ]:          80 :                 src_pe->elemoid == pe->destvertexid)
     469                 :             :             {
     470                 :          24 :                 List       *src_quals = copyObject(pe->dest_quals);
     471                 :          24 :                 List       *dest_quals = copyObject(pe->src_quals);
     472                 :             :                 Expr       *rev_edge_qual;
     473                 :             : 
     474                 :             :                 /* Swap the source and destination varnos in the quals. */
     475                 :          24 :                 ChangeVarNodes((Node *) dest_quals, pe->path_factor->src_pf->factorpos + 1,
     476                 :          24 :                                pe->path_factor->dest_pf->factorpos + 1, 0);
     477                 :          24 :                 ChangeVarNodes((Node *) src_quals, pe->path_factor->dest_pf->factorpos + 1,
     478                 :          24 :                                pe->path_factor->src_pf->factorpos + 1, 0);
     479                 :             : 
     480                 :          24 :                 rev_edge_qual = makeBoolExpr(AND_EXPR, list_concat(src_quals, dest_quals), -1);
     481         [ +  + ]:          24 :                 if (edge_qual)
     482                 :          16 :                     edge_qual = makeBoolExpr(OR_EXPR, list_make2(edge_qual, rev_edge_qual), -1);
     483                 :             :                 else
     484                 :           8 :                     edge_qual = rev_edge_qual;
     485                 :             :             }
     486                 :             : 
     487                 :             :             /*
     488                 :             :              * If the given edge element does not connect the adjacent vertex
     489                 :             :              * elements in this path, the path is broken. Abandon this path as
     490                 :             :              * it won't return any rows.
     491                 :             :              */
     492         [ +  + ]:       24353 :             if (edge_qual == NULL)
     493                 :       21380 :                 return NULL;
     494                 :             : 
     495                 :        2973 :             qual_exprs = lappend(qual_exprs, edge_qual);
     496                 :             :         }
     497                 :             :         else
     498                 :             :             Assert(!pe->src_quals && !pe->dest_quals);
     499                 :             : 
     500                 :             :         /*
     501                 :             :          * Create RangeTblEntry for this element table.
     502                 :             :          *
     503                 :             :          * SQL/PGQ standard (Ref. Section 11.19, Access rule 2 and General
     504                 :             :          * rule 4) does not specify whose access privileges to use when
     505                 :             :          * accessing the element tables: property graph owner's or current
     506                 :             :          * user's. It is safer to use current user's privileges to avoid
     507                 :             :          * unprivileged data access through a property graph. This is inline
     508                 :             :          * with the views being security_invoker by default.
     509                 :             :          */
     510                 :       28063 :         rel = table_open(pe->reloid, AccessShareLock);
     511                 :       28063 :         pni = addRangeTableEntryForRelation(make_parsestate(NULL), rel, AccessShareLock,
     512                 :             :                                             NULL, true, false);
     513                 :       28063 :         table_close(rel, NoLock);
     514                 :       28063 :         path_query->rtable = lappend(path_query->rtable, pni->p_rte);
     515                 :       28063 :         path_query->rteperminfos = lappend(path_query->rteperminfos, pni->p_perminfo);
     516                 :       28063 :         pni->p_rte->perminfoindex = list_length(path_query->rteperminfos);
     517                 :       28063 :         rtr = makeNode(RangeTblRef);
     518                 :       28063 :         rtr->rtindex = list_length(path_query->rtable);
     519                 :       28063 :         fromlist = lappend(fromlist, rtr);
     520                 :             : 
     521                 :             :         /*
     522                 :             :          * Make sure that the assumption mentioned in create_pe_for_element()
     523                 :             :          * holds true; that the elements' RangeTblEntrys are added in the
     524                 :             :          * order in which their respective path factors appear in the list of
     525                 :             :          * path factors representing the path pattern.
     526                 :             :          */
     527                 :             :         Assert(pf->factorpos + 1 == rtr->rtindex);
     528                 :             : 
     529         [ +  + ]:       28063 :         if (pf->whereClause)
     530                 :             :         {
     531                 :             :             Node       *tr;
     532                 :             : 
     533                 :        3752 :             tr = replace_property_refs(rte->relid, pf->whereClause, list_make1(pe));
     534                 :             : 
     535                 :        3752 :             qual_exprs = lappend(qual_exprs, tr);
     536                 :             :         }
     537                 :             :     }
     538                 :             : 
     539         [ +  + ]:         861 :     if (rte->graph_pattern->whereClause)
     540                 :             :     {
     541                 :         335 :         Node       *path_quals = replace_property_refs(rte->relid,
     542                 :         335 :                                                        (Node *) rte->graph_pattern->whereClause,
     543                 :             :                                                        graph_path);
     544                 :             : 
     545                 :         335 :         qual_exprs = lappend(qual_exprs, path_quals);
     546                 :             :     }
     547                 :             : 
     548         [ +  + ]:        1610 :     path_query->jointree = makeFromExpr(fromlist,
     549                 :         749 :                                         qual_exprs ? (Node *) makeBoolExpr(AND_EXPR, qual_exprs, -1) : NULL);
     550                 :             : 
     551                 :             :     /* Construct query targetlist from COLUMNS specification of GRAPH_TABLE. */
     552                 :         861 :     path_query->targetList = castNode(List,
     553                 :             :                                       replace_property_refs(rte->relid,
     554                 :             :                                                             (Node *) rte->graph_table_columns,
     555                 :             :                                                             graph_path));
     556                 :             : 
     557                 :             :     /*
     558                 :             :      * Mark the columns being accessed in the path query as requiring SELECT
     559                 :             :      * privilege. Any lateral columns should have been handled when the
     560                 :             :      * corresponding ColumnRefs were transformed. Ignore those here.
     561                 :             :      */
     562                 :         841 :     vars = pull_vars_of_level((Node *) list_make2(qual_exprs, path_query->targetList), 0);
     563   [ +  +  +  +  :        9709 :     foreach_node(Var, var, vars)
                   +  + ]
     564                 :             :     {
     565                 :        8027 :         RTEPermissionInfo *perminfo = getRTEPermissionInfo(path_query->rteperminfos,
     566                 :        8027 :                                                            rt_fetch(var->varno, path_query->rtable));
     567                 :             : 
     568                 :             :         /* Must offset the attnum to fit in a bitmapset */
     569                 :        8027 :         perminfo->selectedCols = bms_add_member(perminfo->selectedCols,
     570                 :        8027 :                                                 var->varattno - FirstLowInvalidHeapAttributeNumber);
     571                 :             :     }
     572                 :             : 
     573                 :         841 :     return path_query;
     574                 :             : }
     575                 :             : 
     576                 :             : /*
     577                 :             :  * Construct a query which would not return any rows.
     578                 :             :  *
     579                 :             :  * More details in the prologue of generate_queries_for_path_pattern().
     580                 :             :  */
     581                 :             : static Query *
     582                 :           4 : generate_query_for_empty_path_pattern(RangeTblEntry *rte)
     583                 :             : {
     584                 :           4 :     Query      *query = makeNode(Query);
     585                 :             : 
     586                 :           4 :     query->commandType = CMD_SELECT;
     587                 :           4 :     query->rtable = NIL;
     588                 :           4 :     query->rteperminfos = NIL;
     589                 :           4 :     query->jointree = makeFromExpr(NIL, (Node *) makeBoolConst(false, false));
     590                 :             : 
     591                 :             :     /*
     592                 :             :      * Even though no rows are returned, the result still projects the same
     593                 :             :      * columns as projected by GRAPH_TABLE clause. Do this by constructing a
     594                 :             :      * target list full of NULL values.
     595                 :             :      */
     596   [ +  -  +  +  :          16 :     foreach_node(TargetEntry, te, rte->graph_table_columns)
                   +  + ]
     597                 :             :     {
     598                 :           8 :         Node       *nte = (Node *) te->expr;
     599                 :             : 
     600                 :           8 :         te->expr = (Expr *) makeNullConst(exprType(nte), exprTypmod(nte), exprCollation(nte));
     601                 :           8 :         query->targetList = lappend(query->targetList, te);
     602                 :             :     }
     603                 :             : 
     604                 :           4 :     return query;
     605                 :             : }
     606                 :             : 
     607                 :             : /*
     608                 :             :  * Construct a query which is UNION of given path queries.
     609                 :             :  *
     610                 :             :  * The UNION query derives collations of its targetlist entries from the
     611                 :             :  * corresponding targetlist entries of the path queries. The targetlists of path
     612                 :             :  * queries being UNION'ed already have collations assigned.  No separate
     613                 :             :  * collation assignment required in this function.
     614                 :             :  *
     615                 :             :  * The function destroys given pathqueries list while constructing
     616                 :             :  * SetOperationStmt recursively. Hence the function always returns with
     617                 :             :  * `pathqueries` set to NIL.
     618                 :             :  */
     619                 :             : static Query *
     620                 :         474 : generate_union_from_pathqueries(List **pathqueries)
     621                 :             : {
     622                 :         474 :     List       *rtable = NIL;
     623                 :         474 :     Query      *sampleQuery = linitial_node(Query, *pathqueries);
     624                 :             :     SetOperationStmt *sostmt;
     625                 :             :     Query      *union_query;
     626                 :             :     int         resno;
     627                 :             :     ListCell   *lctl,
     628                 :             :                *lct,
     629                 :             :                *lcm,
     630                 :             :                *lcc;
     631                 :             : 
     632                 :             :     Assert(list_length(*pathqueries) > 0);
     633                 :             : 
     634                 :             :     /* If there's only one pathquery, no need to construct a UNION query. */
     635         [ +  + ]:         474 :     if (list_length(*pathqueries) == 1)
     636                 :             :     {
     637                 :         343 :         *pathqueries = NIL;
     638                 :         343 :         return sampleQuery;
     639                 :             :     }
     640                 :             : 
     641                 :         131 :     sostmt = castNode(SetOperationStmt,
     642                 :             :                       generate_setop_from_pathqueries(*pathqueries, &rtable, NULL));
     643                 :             : 
     644                 :             :     /* Encapsulate the set operation statement into a Query. */
     645                 :         131 :     union_query = makeNode(Query);
     646                 :         131 :     union_query->commandType = CMD_SELECT;
     647                 :         131 :     union_query->rtable = rtable;
     648                 :         131 :     union_query->setOperations = (Node *) sostmt;
     649                 :         131 :     union_query->rteperminfos = NIL;
     650                 :         131 :     union_query->jointree = makeFromExpr(NIL, NULL);
     651                 :             : 
     652                 :             :     /*
     653                 :             :      * Generate dummy targetlist for outer query using column names from one
     654                 :             :      * of the queries and common datatypes/collations of topmost set
     655                 :             :      * operation.  It shouldn't matter which query. Also it shouldn't matter
     656                 :             :      * which RT index is used as varno in the target list entries, as long as
     657                 :             :      * it corresponds to a real RT entry; else funny things may happen when
     658                 :             :      * the tree is mashed by rule rewriting. So we use 1 since there's always
     659                 :             :      * one RT entry at least.
     660                 :             :      */
     661                 :             :     Assert(rt_fetch(1, rtable));
     662                 :         131 :     union_query->targetList = NULL;
     663                 :         131 :     resno = 1;
     664   [ +  -  +  +  :         480 :     forfour(lct, sostmt->colTypes,
          +  -  +  +  +  
          -  +  +  +  -  
          +  +  +  +  +  
          -  +  -  +  -  
                   +  + ]
     665                 :             :             lcm, sostmt->colTypmods,
     666                 :             :             lcc, sostmt->colCollations,
     667                 :             :             lctl, sampleQuery->targetList)
     668                 :             :     {
     669                 :         349 :         Oid         colType = lfirst_oid(lct);
     670                 :         349 :         int32       colTypmod = lfirst_int(lcm);
     671                 :         349 :         Oid         colCollation = lfirst_oid(lcc);
     672                 :         349 :         TargetEntry *sample_tle = (TargetEntry *) lfirst(lctl);
     673                 :             :         char       *colName;
     674                 :             :         TargetEntry *tle;
     675                 :             :         Var        *var;
     676                 :             : 
     677                 :             :         Assert(!sample_tle->resjunk);
     678                 :         349 :         colName = pstrdup(sample_tle->resname);
     679                 :         349 :         var = makeVar(1, sample_tle->resno, colType, colTypmod, colCollation, 0);
     680                 :         349 :         var->location = exprLocation((Node *) sample_tle->expr);
     681                 :         349 :         tle = makeTargetEntry((Expr *) var, (AttrNumber) resno++, colName, false);
     682                 :         349 :         union_query->targetList = lappend(union_query->targetList, tle);
     683                 :             :     }
     684                 :             : 
     685                 :         131 :     *pathqueries = NIL;
     686                 :         131 :     return union_query;
     687                 :             : }
     688                 :             : 
     689                 :             : /*
     690                 :             :  * Construct a query which is UNION of all the given path queries.
     691                 :             :  *
     692                 :             :  * The function destroys given pathqueries list while constructing
     693                 :             :  * SetOperationStmt recursively.
     694                 :             :  */
     695                 :             : static Node *
     696                 :         633 : generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist)
     697                 :             : {
     698                 :             :     SetOperationStmt *sostmt;
     699                 :             :     Query      *lquery;
     700                 :             :     Node       *rarg;
     701                 :         633 :     RangeTblRef *lrtr = makeNode(RangeTblRef);
     702                 :             :     List       *rtargetlist;
     703                 :             :     ParseNamespaceItem *pni;
     704                 :             : 
     705                 :             :     /* Guard against stack overflow due to many path queries. */
     706                 :         633 :     check_stack_depth();
     707                 :             : 
     708                 :             :     /* Recursion termination condition. */
     709         [ +  + ]:         633 :     if (list_length(pathqueries) == 0)
     710                 :             :     {
     711                 :         131 :         *targetlist = NIL;
     712                 :         131 :         return NULL;
     713                 :             :     }
     714                 :             : 
     715                 :         502 :     lquery = linitial_node(Query, pathqueries);
     716                 :             : 
     717                 :             :     /*
     718                 :             :      * Each path query will become a subquery of the UNION statement. So any
     719                 :             :      * Vars that already refer outside the path query must be adjusted for
     720                 :             :      * additional query level.
     721                 :             :      */
     722                 :         502 :     IncrementVarSublevelsUp((Node *) lquery, 1, 1);
     723                 :             : 
     724                 :         502 :     pni = addRangeTableEntryForSubquery(make_parsestate(NULL), lquery, NULL,
     725                 :             :                                         false, false);
     726                 :         502 :     *rtable = lappend(*rtable, pni->p_rte);
     727                 :         502 :     lrtr->rtindex = list_length(*rtable);
     728                 :         502 :     rarg = generate_setop_from_pathqueries(list_delete_first(pathqueries), rtable, &rtargetlist);
     729         [ +  + ]:         502 :     if (rarg == NULL)
     730                 :             :     {
     731                 :             :         /*
     732                 :             :          * No further path queries in the list. Convert the last query into a
     733                 :             :          * RangeTblRef as expected by SetOperationStmt. Extract a list of the
     734                 :             :          * non-junk TLEs for upper-level processing.
     735                 :             :          */
     736         [ +  - ]:         131 :         if (targetlist)
     737                 :             :         {
     738                 :         131 :             *targetlist = NIL;
     739   [ +  -  +  +  :         611 :             foreach_node(TargetEntry, tle, lquery->targetList)
                   +  + ]
     740                 :             :             {
     741         [ +  - ]:         349 :                 if (!tle->resjunk)
     742                 :         349 :                     *targetlist = lappend(*targetlist, tle);
     743                 :             :             }
     744                 :             :         }
     745                 :         131 :         return (Node *) lrtr;
     746                 :             :     }
     747                 :             : 
     748                 :         371 :     sostmt = makeNode(SetOperationStmt);
     749                 :         371 :     sostmt->op = SETOP_UNION;
     750                 :         371 :     sostmt->all = true;
     751                 :         371 :     sostmt->larg = (Node *) lrtr;
     752                 :         371 :     sostmt->rarg = rarg;
     753                 :         371 :     constructSetOpTargetlist(NULL, sostmt, lquery->targetList, rtargetlist, targetlist, "UNION", false);
     754                 :             : 
     755                 :         371 :     return (Node *) sostmt;
     756                 :             : }
     757                 :             : 
     758                 :             : /*
     759                 :             :  * Construct a path_element object for the graph element given by `elemoid`
     760                 :             :  * satisfied by the path factor `pf`.
     761                 :             :  *
     762                 :             :  * If the type of graph element does not fit the element pattern kind, the
     763                 :             :  * function returns NULL.
     764                 :             :  */
     765                 :             : static struct path_element *
     766                 :        4633 : create_pe_for_element(struct path_factor *pf, Oid elemoid)
     767                 :             : {
     768                 :        4633 :     HeapTuple   eletup = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(elemoid));
     769                 :             :     Form_pg_propgraph_element pgeform;
     770                 :             :     struct path_element *pe;
     771                 :             : 
     772         [ -  + ]:        4633 :     if (!eletup)
     773         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for property graph element %u", elemoid);
     774                 :        4633 :     pgeform = ((Form_pg_propgraph_element) GETSTRUCT(eletup));
     775                 :             : 
     776   [ +  +  +  + ]:        4633 :     if ((pgeform->pgekind == PGEKIND_VERTEX && pf->kind != VERTEX_PATTERN) ||
     777   [ +  +  +  +  :        4017 :         (pgeform->pgekind == PGEKIND_EDGE && !IS_EDGE_PATTERN(pf->kind)))
             +  +  +  + ]
     778                 :             :     {
     779                 :        1912 :         ReleaseSysCache(eletup);
     780                 :        1912 :         return NULL;
     781                 :             :     }
     782                 :             : 
     783                 :        2721 :     pe = palloc0_object(struct path_element);
     784                 :        2721 :     pe->path_factor = pf;
     785                 :        2721 :     pe->elemoid = elemoid;
     786                 :        2721 :     pe->reloid = pgeform->pgerelid;
     787                 :             : 
     788                 :             :     /*
     789                 :             :      * When a path is converted into a query
     790                 :             :      * (generate_query_for_graph_path()), a RangeTblEntry will be created for
     791                 :             :      * every element in the path.  Fixing rtindexes of RangeTblEntrys here
     792                 :             :      * makes it possible to craft elements' qual expressions only once while
     793                 :             :      * we have access to the catalog entry. Otherwise they need to be crafted
     794                 :             :      * as many times as the number of paths a given element appears in,
     795                 :             :      * fetching catalog entry again each time.  Hence we simply assume
     796                 :             :      * RangeTblEntrys will be created in the same order in which the
     797                 :             :      * corresponding path factors appear in the list of path factors
     798                 :             :      * representing a path pattern. That way their rtindexes will be same as
     799                 :             :      * path_factor::factorpos + 1.
     800                 :             :      */
     801   [ +  +  +  +  :        2721 :     if (IS_EDGE_PATTERN(pf->kind))
                   +  + ]
     802                 :             :     {
     803                 :        1245 :         pe->srcvertexid = pgeform->pgesrcvertexid;
     804                 :        1245 :         pe->destvertexid = pgeform->pgedestvertexid;
     805                 :             :         Assert(pf->src_pf && pf->dest_pf);
     806                 :             : 
     807                 :        1245 :         pe->src_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->src_pf->factorpos + 1,
     808                 :             :                                                      pe->srcvertexid,
     809                 :             :                                                      Anum_pg_propgraph_element_pgesrckey,
     810                 :             :                                                      Anum_pg_propgraph_element_pgesrcref,
     811                 :             :                                                      Anum_pg_propgraph_element_pgesrceqop);
     812                 :        1245 :         pe->dest_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->dest_pf->factorpos + 1,
     813                 :             :                                                       pe->destvertexid,
     814                 :             :                                                       Anum_pg_propgraph_element_pgedestkey,
     815                 :             :                                                       Anum_pg_propgraph_element_pgedestref,
     816                 :             :                                                       Anum_pg_propgraph_element_pgedesteqop);
     817                 :             :     }
     818                 :             : 
     819                 :        2721 :     ReleaseSysCache(eletup);
     820                 :             : 
     821                 :        2721 :     return pe;
     822                 :             : }
     823                 :             : 
     824                 :             : /*
     825                 :             :  * Returns the list of OIDs of graph labels which the given label expression
     826                 :             :  * resolves to in the given property graph.
     827                 :             :  */
     828                 :             : static List *
     829                 :        1364 : get_labels_for_expr(Oid propgraphid, Node *labelexpr)
     830                 :             : {
     831                 :             :     List       *label_oids;
     832                 :             : 
     833         [ +  + ]:        1364 :     if (!labelexpr)
     834                 :             :     {
     835                 :             :         Relation    rel;
     836                 :             :         SysScanDesc scan;
     837                 :             :         ScanKeyData key[1];
     838                 :             :         HeapTuple   tup;
     839                 :             : 
     840                 :             :         /*
     841                 :             :          * According to section 9.2 "Contextual inference of a set of labels"
     842                 :             :          * subclause 2.a.ii of SQL/PGQ standard, element pattern which does
     843                 :             :          * not have a label expression is considered to have label expression
     844                 :             :          * equivalent to '%|!%' which is set of all labels.
     845                 :             :          */
     846                 :         408 :         label_oids = NIL;
     847                 :         408 :         rel = table_open(PropgraphLabelRelationId, AccessShareLock);
     848                 :         408 :         ScanKeyInit(&key[0],
     849                 :             :                     Anum_pg_propgraph_label_pglpgid,
     850                 :             :                     BTEqualStrategyNumber,
     851                 :             :                     F_OIDEQ, ObjectIdGetDatum(propgraphid));
     852                 :         408 :         scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId,
     853                 :             :                                   true, NULL, 1, key);
     854         [ +  + ]:        2728 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     855                 :             :         {
     856                 :        2320 :             Form_pg_propgraph_label label = (Form_pg_propgraph_label) GETSTRUCT(tup);
     857                 :             : 
     858                 :        2320 :             label_oids = lappend_oid(label_oids, label->oid);
     859                 :             :         }
     860                 :         408 :         systable_endscan(scan);
     861                 :         408 :         table_close(rel, AccessShareLock);
     862                 :             :     }
     863         [ +  + ]:         956 :     else if (IsA(labelexpr, GraphLabelRef))
     864                 :             :     {
     865                 :         910 :         GraphLabelRef *glr = castNode(GraphLabelRef, labelexpr);
     866                 :             : 
     867                 :         910 :         label_oids = list_make1_oid(glr->labelid);
     868                 :             :     }
     869         [ +  - ]:          46 :     else if (IsA(labelexpr, BoolExpr))
     870                 :             :     {
     871                 :          46 :         BoolExpr   *be = castNode(BoolExpr, labelexpr);
     872                 :          46 :         List       *label_exprs = be->args;
     873                 :             : 
     874                 :          46 :         label_oids = NIL;
     875   [ +  -  +  +  :         188 :         foreach_node(GraphLabelRef, glr, label_exprs)
                   +  + ]
     876                 :          96 :             label_oids = lappend_oid(label_oids, glr->labelid);
     877                 :             :     }
     878                 :             :     else
     879                 :             :     {
     880                 :             :         /*
     881                 :             :          * should not reach here since gram.y will not generate a label
     882                 :             :          * expression with other node types.
     883                 :             :          */
     884         [ #  # ]:           0 :         elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
     885                 :             :     }
     886                 :             : 
     887                 :        1364 :     return label_oids;
     888                 :             : }
     889                 :             : 
     890                 :             : /*
     891                 :             :  * Return a list of all the graph elements that satisfy the graph element pattern
     892                 :             :  * represented by the given path_factor `pf`.
     893                 :             :  *
     894                 :             :  * First we find all the graph labels that satisfy the label expression in path
     895                 :             :  * factor. Each label is associated with one or more graph elements.  A union of
     896                 :             :  * all such elements satisfies the element pattern. We create one path_element
     897                 :             :  * object representing every element whose graph element kind qualifies the
     898                 :             :  * element pattern kind. A list of all such path_element objects is returned.
     899                 :             :  *
     900                 :             :  * Note that we need to report an error for an explicitly specified label which
     901                 :             :  * is not associated with any graph element of the required kind. So we have to
     902                 :             :  * treat each label separately. Without that requirement we could have collected
     903                 :             :  * all the unique elements first and then created path_element objects for them
     904                 :             :  * to simplify the code.
     905                 :             :  */
     906                 :             : static List *
     907                 :        1364 : get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf)
     908                 :             : {
     909                 :        1364 :     List       *label_oids = get_labels_for_expr(propgraphid, pf->labelexpr);
     910                 :        1364 :     List       *elem_oids_seen = NIL;
     911                 :        1364 :     List       *pf_elem_oids = NIL;
     912                 :        1364 :     List       *path_elements = NIL;
     913                 :        1364 :     List       *unresolved_labels = NIL;
     914                 :             :     Relation    rel;
     915                 :             :     SysScanDesc scan;
     916                 :             :     ScanKeyData key[1];
     917                 :             :     HeapTuple   tup;
     918                 :             : 
     919                 :             :     /*
     920                 :             :      * A property graph element can be either a vertex or an edge. Other types
     921                 :             :      * of path factors like nested path pattern need to be handled separately
     922                 :             :      * when supported.
     923                 :             :      */
     924                 :             :     Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
     925                 :             : 
     926                 :        1364 :     rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
     927   [ +  -  +  +  :        6034 :     foreach_oid(labeloid, label_oids)
                   +  + ]
     928                 :             :     {
     929                 :        3322 :         bool        found = false;
     930                 :             : 
     931                 :        3322 :         ScanKeyInit(&key[0],
     932                 :             :                     Anum_pg_propgraph_element_label_pgellabelid,
     933                 :             :                     BTEqualStrategyNumber,
     934                 :             :                     F_OIDEQ, ObjectIdGetDatum(labeloid));
     935                 :        3322 :         scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true,
     936                 :             :                                   NULL, 1, key);
     937         [ +  + ]:       11025 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     938                 :             :         {
     939                 :        7703 :             Form_pg_propgraph_element_label label_elem = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
     940                 :        7703 :             Oid         elem_oid = label_elem->pgelelid;
     941                 :             : 
     942         [ +  + ]:        7703 :             if (!list_member_oid(elem_oids_seen, elem_oid))
     943                 :             :             {
     944                 :             :                 /*
     945                 :             :                  * Create path_element object if the new element qualifies the
     946                 :             :                  * element pattern kind.
     947                 :             :                  */
     948                 :        4633 :                 struct path_element *pe = create_pe_for_element(pf, elem_oid);
     949                 :             : 
     950         [ +  + ]:        4633 :                 if (pe)
     951                 :             :                 {
     952                 :        2721 :                     path_elements = lappend(path_elements, pe);
     953                 :             : 
     954                 :             :                     /* Remember qualified elements. */
     955                 :        2721 :                     pf_elem_oids = lappend_oid(pf_elem_oids, elem_oid);
     956                 :        2721 :                     found = true;
     957                 :             :                 }
     958                 :             : 
     959                 :             :                 /*
     960                 :             :                  * Remember qualified and unqualified elements processed so
     961                 :             :                  * far to avoid processing already processed elements again.
     962                 :             :                  */
     963                 :        4633 :                 elem_oids_seen = lappend_oid(elem_oids_seen, label_elem->pgelelid);
     964                 :             :             }
     965         [ +  + ]:        3070 :             else if (list_member_oid(pf_elem_oids, elem_oid))
     966                 :             :             {
     967                 :             :                 /*
     968                 :             :                  * The graph element is known to qualify the given element
     969                 :             :                  * pattern. Flag that the current label has at least one
     970                 :             :                  * qualified element associated with it.
     971                 :             :                  */
     972                 :        1574 :                 found = true;
     973                 :             :             }
     974                 :             :         }
     975                 :             : 
     976         [ +  + ]:        3322 :         if (!found)
     977                 :             :         {
     978                 :             :             /*
     979                 :             :              * We did not find any qualified element associated with this
     980                 :             :              * label. The label or its properties can not be associated with
     981                 :             :              * the given element pattern. Throw an error if the label was
     982                 :             :              * explicitly specified in the element pattern. Otherwise remember
     983                 :             :              * it for later use.
     984                 :             :              */
     985         [ +  + ]:         984 :             if (!pf->labelexpr)
     986                 :         976 :                 unresolved_labels = lappend_oid(unresolved_labels, labeloid);
     987                 :             :             else
     988   [ +  -  +  - ]:           8 :                 ereport(ERROR,
     989                 :             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     990                 :             :                          errmsg("no property graph element of type \"%s\" has label \"%s\" associated with it in property graph \"%s\"",
     991                 :             :                                 pf->kind == VERTEX_PATTERN ? "vertex" : "edge",
     992                 :             :                                 get_propgraph_label_name(labeloid),
     993                 :             :                                 get_rel_name(propgraphid))));
     994                 :             :         }
     995                 :             : 
     996                 :        3314 :         systable_endscan(scan);
     997                 :             :     }
     998                 :        1356 :     table_close(rel, AccessShareLock);
     999                 :             : 
    1000                 :             :     /*
    1001                 :             :      * Remove the labels which were not explicitly mentioned in the label
    1002                 :             :      * expression but do not have any qualified elements associated with them.
    1003                 :             :      * Properties associated with such labels may not be referenced. See
    1004                 :             :      * replace_property_refs_mutator() for more details.
    1005                 :             :      */
    1006                 :        1356 :     pf->labeloids = list_difference_oid(label_oids, unresolved_labels);
    1007                 :             : 
    1008                 :        1356 :     return path_elements;
    1009                 :             : }
    1010                 :             : 
    1011                 :             : /*
    1012                 :             :  * Mutating property references into table variables
    1013                 :             :  */
    1014                 :             : 
    1015                 :             : struct replace_property_refs_context
    1016                 :             : {
    1017                 :             :     Oid         propgraphid;
    1018                 :             :     const List *mappings;
    1019                 :             : };
    1020                 :             : 
    1021                 :             : static Node *
    1022                 :       50288 : replace_property_refs_mutator(Node *node, struct replace_property_refs_context *context)
    1023                 :             : {
    1024         [ -  + ]:       50288 :     if (node == NULL)
    1025                 :           0 :         return NULL;
    1026         [ +  + ]:       50288 :     if (IsA(node, Var))
    1027                 :             :     {
    1028                 :          56 :         Var        *var = (Var *) node;
    1029                 :          56 :         Var        *newvar = copyObject(var);
    1030                 :             : 
    1031                 :             :         /*
    1032                 :             :          * If it's already a Var, then it was a lateral reference.  Since we
    1033                 :             :          * are in a subquery after the rewrite, we have to increase the level
    1034                 :             :          * by one.
    1035                 :             :          */
    1036                 :          56 :         newvar->varlevelsup++;
    1037                 :             : 
    1038                 :          56 :         return (Node *) newvar;
    1039                 :             :     }
    1040         [ +  + ]:       50232 :     else if (IsA(node, GraphPropertyRef))
    1041                 :             :     {
    1042                 :       10935 :         GraphPropertyRef *gpr = (GraphPropertyRef *) node;
    1043                 :       10935 :         Node       *n = NULL;
    1044                 :       10935 :         struct path_element *found_mapping = NULL;
    1045                 :       10935 :         struct path_factor *mapping_factor = NULL;
    1046                 :       10935 :         List       *unrelated_labels = NIL;
    1047                 :             : 
    1048   [ +  -  +  -  :       25093 :         foreach_ptr(struct path_element, m, context->mappings)
                   +  + ]
    1049                 :             :         {
    1050   [ +  +  +  + ]:       14158 :             if (m->path_factor->variable && strcmp(gpr->elvarname, m->path_factor->variable) == 0)
    1051                 :             :             {
    1052                 :       10935 :                 found_mapping = m;
    1053                 :       10935 :                 break;
    1054                 :             :             }
    1055                 :             :         }
    1056                 :             : 
    1057                 :             :         /*
    1058                 :             :          * transformGraphTablePropertyRef() would not create a
    1059                 :             :          * GraphPropertyRef for a variable which is not present in the graph
    1060                 :             :          * path pattern.
    1061                 :             :          */
    1062                 :             :         Assert(found_mapping);
    1063                 :             : 
    1064                 :       10935 :         mapping_factor = found_mapping->path_factor;
    1065                 :             : 
    1066                 :             :         /*
    1067                 :             :          * Find property definition for given element through any of the
    1068                 :             :          * associated labels qualifying the given element pattern.
    1069                 :             :          */
    1070   [ +  -  +  +  :       59845 :         foreach_oid(labeloid, mapping_factor->labeloids)
                   +  + ]
    1071                 :             :         {
    1072                 :       37975 :             Oid         elem_labelid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
    1073                 :             :                                                        Anum_pg_propgraph_element_label_oid,
    1074                 :             :                                                        ObjectIdGetDatum(found_mapping->elemoid),
    1075                 :             :                                                        ObjectIdGetDatum(labeloid));
    1076                 :             : 
    1077         [ +  + ]:       37975 :             if (OidIsValid(elem_labelid))
    1078                 :             :             {
    1079                 :       22995 :                 HeapTuple   tup = SearchSysCache2(PROPGRAPHLABELPROP, ObjectIdGetDatum(elem_labelid),
    1080                 :             :                                                   ObjectIdGetDatum(gpr->propid));
    1081                 :             : 
    1082         [ +  + ]:       22995 :                 if (!tup)
    1083                 :             :                 {
    1084                 :             :                     /*
    1085                 :             :                      * The label is associated with the given element but it
    1086                 :             :                      * is not associated with the required property. Check
    1087                 :             :                      * next label.
    1088                 :             :                      */
    1089                 :       11720 :                     continue;
    1090                 :             :                 }
    1091                 :             : 
    1092                 :       11275 :                 n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHLABELPROP,
    1093                 :             :                                                                             tup, Anum_pg_propgraph_label_property_plpexpr)));
    1094                 :       11275 :                 ChangeVarNodes(n, 1, mapping_factor->factorpos + 1, 0);
    1095                 :             : 
    1096                 :       11275 :                 ReleaseSysCache(tup);
    1097                 :             :             }
    1098                 :             :             else
    1099                 :             :             {
    1100                 :             :                 /*
    1101                 :             :                  * Label is not associated with the element but it may be
    1102                 :             :                  * associated with the property through some other element.
    1103                 :             :                  * Save it for later use.
    1104                 :             :                  */
    1105                 :       14980 :                 unrelated_labels = lappend_oid(unrelated_labels, labeloid);
    1106                 :             :             }
    1107                 :             :         }
    1108                 :             : 
    1109                 :             :         /* See if we can resolve the property in some other way. */
    1110         [ +  + ]:       10935 :         if (!n)
    1111                 :             :         {
    1112                 :          72 :             bool        prop_associated = false;
    1113                 :             : 
    1114   [ +  +  +  +  :         168 :             foreach_oid(loid, unrelated_labels)
                   +  + ]
    1115                 :             :             {
    1116         [ +  + ]:          76 :                 if (is_property_associated_with_label(loid, gpr->propid))
    1117                 :             :                 {
    1118                 :          52 :                     prop_associated = true;
    1119                 :          52 :                     break;
    1120                 :             :                 }
    1121                 :             :             }
    1122                 :             : 
    1123         [ +  + ]:          72 :             if (prop_associated)
    1124                 :             :             {
    1125                 :             :                 /*
    1126                 :             :                  * The property is associated with at least one of the labels
    1127                 :             :                  * that satisfy given element pattern. If it's associated with
    1128                 :             :                  * the given element (through some other label), use
    1129                 :             :                  * corresponding value expression. Otherwise NULL. Ref.
    1130                 :             :                  * SQL/PGQ standard section 6.5 Property Reference, General
    1131                 :             :                  * Rule 2.b.
    1132                 :             :                  */
    1133                 :          52 :                 n = get_element_property_expr(found_mapping->elemoid, gpr->propid,
    1134                 :          52 :                                               mapping_factor->factorpos + 1);
    1135                 :             : 
    1136         [ +  + ]:          52 :                 if (!n)
    1137                 :          48 :                     n = (Node *) makeNullConst(gpr->typeId, gpr->typmod, gpr->collation);
    1138                 :             :             }
    1139                 :             : 
    1140                 :             :         }
    1141                 :             : 
    1142         [ +  + ]:       10935 :         if (!n)
    1143         [ +  - ]:          20 :             ereport(ERROR,
    1144                 :             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1145                 :             :                     errmsg("property \"%s\" for element variable \"%s\" not found",
    1146                 :             :                            get_propgraph_property_name(gpr->propid), mapping_factor->variable));
    1147                 :             : 
    1148                 :       10915 :         return n;
    1149                 :             :     }
    1150                 :             : 
    1151                 :       39297 :     return expression_tree_mutator(node, replace_property_refs_mutator, context);
    1152                 :             : }
    1153                 :             : 
    1154                 :             : static Node *
    1155                 :        4948 : replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
    1156                 :             : {
    1157                 :             :     struct replace_property_refs_context context;
    1158                 :             : 
    1159                 :        4948 :     context.mappings = mappings;
    1160                 :        4948 :     context.propgraphid = propgraphid;
    1161                 :             : 
    1162                 :        4948 :     return replace_property_refs_mutator(node, &context);
    1163                 :             : }
    1164                 :             : 
    1165                 :             : /*
    1166                 :             :  * Build join qualification expressions between edge and vertex tables.
    1167                 :             :  */
    1168                 :             : static List *
    1169                 :        2490 : 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)
    1170                 :             : {
    1171                 :        2490 :     List       *quals = NIL;
    1172                 :             :     Form_pg_propgraph_element pgeform;
    1173                 :             :     Datum       datum;
    1174                 :             :     Datum      *d1,
    1175                 :             :                *d2,
    1176                 :             :                *d3;
    1177                 :             :     int         n1,
    1178                 :             :                 n2,
    1179                 :             :                 n3;
    1180                 :        2490 :     ParseState *pstate = make_parsestate(NULL);
    1181                 :        2490 :     Oid         refrelid = GetSysCacheOid1(PROPGRAPHELOID, Anum_pg_propgraph_element_pgerelid, ObjectIdGetDatum(refid));
    1182                 :             : 
    1183                 :        2490 :     pgeform = (Form_pg_propgraph_element) GETSTRUCT(edgetup);
    1184                 :             : 
    1185                 :        2490 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_key_attnum);
    1186                 :        2490 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d1, NULL, &n1);
    1187                 :             : 
    1188                 :        2490 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_ref_attnum);
    1189                 :        2490 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d2, NULL, &n2);
    1190                 :             : 
    1191                 :        2490 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_eqop_attnum);
    1192                 :        2490 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &d3, NULL, &n3);
    1193                 :             : 
    1194         [ -  + ]:        2490 :     if (n1 != n2)
    1195         [ #  # ]:           0 :         elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
    1196         [ -  + ]:        2490 :     if (n1 != n3)
    1197         [ #  # ]:           0 :         elog(ERROR, "array size key (%d) vs operator (%d) mismatch for element ID %u", catalog_key_attnum, catalog_eqop_attnum, pgeform->oid);
    1198                 :             : 
    1199         [ +  + ]:        5640 :     for (int i = 0; i < n1; i++)
    1200                 :             :     {
    1201                 :        3150 :         AttrNumber  keyattn = DatumGetInt16(d1[i]);
    1202                 :        3150 :         AttrNumber  refattn = DatumGetInt16(d2[i]);
    1203                 :        3150 :         Oid         eqop = DatumGetObjectId(d3[i]);
    1204                 :             :         Var        *keyvar;
    1205                 :             :         Var        *refvar;
    1206                 :             :         Oid         atttypid;
    1207                 :             :         int32       atttypmod;
    1208                 :             :         Oid         attcoll;
    1209                 :             :         HeapTuple   tup;
    1210                 :             :         Form_pg_operator opform;
    1211                 :             :         List       *args;
    1212                 :             :         Oid         actual_arg_types[2];
    1213                 :             :         Oid         declared_arg_types[2];
    1214                 :             :         OpExpr     *linkqual;
    1215                 :             : 
    1216                 :        3150 :         get_atttypetypmodcoll(pgeform->pgerelid, keyattn, &atttypid, &atttypmod, &attcoll);
    1217                 :        3150 :         keyvar = makeVar(edgerti, keyattn, atttypid, atttypmod, attcoll, 0);
    1218                 :        3150 :         get_atttypetypmodcoll(refrelid, refattn, &atttypid, &atttypmod, &attcoll);
    1219                 :        3150 :         refvar = makeVar(refrti, refattn, atttypid, atttypmod, attcoll, 0);
    1220                 :             : 
    1221                 :        3150 :         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(eqop));
    1222         [ -  + ]:        3150 :         if (!HeapTupleIsValid(tup))
    1223         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for operator %u", eqop);
    1224                 :        3150 :         opform = (Form_pg_operator) GETSTRUCT(tup);
    1225                 :             :         /* An equality operator is a binary operator returning boolean result. */
    1226                 :             :         Assert(opform->oprkind == 'b'
    1227                 :             :                && RegProcedureIsValid(opform->oprcode)
    1228                 :             :                && opform->oprresult == BOOLOID
    1229                 :             :                && !get_func_retset(opform->oprcode));
    1230                 :             : 
    1231                 :             :         /*
    1232                 :             :          * Prepare operands and cast them to the types required by the
    1233                 :             :          * equality operator. Similar to PK/FK quals, referenced vertex key is
    1234                 :             :          * used as left operand and referencing edge key is used as right
    1235                 :             :          * operand.
    1236                 :             :          */
    1237                 :        3150 :         args = list_make2(refvar, keyvar);
    1238                 :        3150 :         actual_arg_types[0] = exprType((Node *) refvar);
    1239                 :        3150 :         actual_arg_types[1] = exprType((Node *) keyvar);
    1240                 :        3150 :         declared_arg_types[0] = opform->oprleft;
    1241                 :        3150 :         declared_arg_types[1] = opform->oprright;
    1242                 :        3150 :         make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
    1243                 :             : 
    1244                 :        3150 :         linkqual = makeNode(OpExpr);
    1245                 :        3150 :         linkqual->opno = opform->oid;
    1246                 :        3150 :         linkqual->opfuncid = opform->oprcode;
    1247                 :        3150 :         linkqual->opresulttype = opform->oprresult;
    1248                 :        3150 :         linkqual->opretset = false;
    1249                 :             :         /* opcollid and inputcollid will be set by parse_collate.c */
    1250                 :        3150 :         linkqual->args = args;
    1251                 :        3150 :         linkqual->location = -1;
    1252                 :             : 
    1253                 :        3150 :         ReleaseSysCache(tup);
    1254                 :        3150 :         quals = lappend(quals, linkqual);
    1255                 :             :     }
    1256                 :             : 
    1257                 :        2490 :     assign_expr_collations(pstate, (Node *) quals);
    1258                 :             : 
    1259                 :        2490 :     return quals;
    1260                 :             : }
    1261                 :             : 
    1262                 :             : /*
    1263                 :             :  * Check if the given property is associated with the given label.
    1264                 :             :  *
    1265                 :             :  * A label projects the same set of properties through every element it is
    1266                 :             :  * associated with. Find any of the elements and return true if that element is
    1267                 :             :  * associated with the given property. False otherwise.
    1268                 :             :  */
    1269                 :             : static bool
    1270                 :          76 : is_property_associated_with_label(Oid labeloid, Oid propoid)
    1271                 :             : {
    1272                 :             :     Relation    rel;
    1273                 :             :     SysScanDesc scan;
    1274                 :             :     ScanKeyData key[1];
    1275                 :             :     HeapTuple   tup;
    1276                 :          76 :     bool        associated = false;
    1277                 :             : 
    1278                 :          76 :     rel = table_open(PropgraphElementLabelRelationId, RowShareLock);
    1279                 :          76 :     ScanKeyInit(&key[0],
    1280                 :             :                 Anum_pg_propgraph_element_label_pgellabelid,
    1281                 :             :                 BTEqualStrategyNumber,
    1282                 :             :                 F_OIDEQ, ObjectIdGetDatum(labeloid));
    1283                 :          76 :     scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId,
    1284                 :             :                               true, NULL, 1, key);
    1285                 :             : 
    1286         [ +  - ]:          76 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
    1287                 :             :     {
    1288                 :          76 :         Form_pg_propgraph_element_label ele_label = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
    1289                 :             : 
    1290                 :          76 :         associated = SearchSysCacheExists2(PROPGRAPHLABELPROP,
    1291                 :             :                                            ObjectIdGetDatum(ele_label->oid), ObjectIdGetDatum(propoid));
    1292                 :             :     }
    1293                 :          76 :     systable_endscan(scan);
    1294                 :          76 :     table_close(rel, RowShareLock);
    1295                 :             : 
    1296                 :          76 :     return associated;
    1297                 :             : }
    1298                 :             : 
    1299                 :             : /*
    1300                 :             :  * If given element has the given property associated with it, through any of
    1301                 :             :  * the associated labels, return value expression of the property. Otherwise
    1302                 :             :  * NULL.
    1303                 :             :  */
    1304                 :             : static Node *
    1305                 :          52 : get_element_property_expr(Oid elemoid, Oid propoid, int rtindex)
    1306                 :             : {
    1307                 :             :     Relation    rel;
    1308                 :             :     SysScanDesc scan;
    1309                 :             :     ScanKeyData key[1];
    1310                 :             :     HeapTuple   labeltup;
    1311                 :          52 :     Node       *n = NULL;
    1312                 :             : 
    1313                 :          52 :     rel = table_open(PropgraphElementLabelRelationId, RowShareLock);
    1314                 :          52 :     ScanKeyInit(&key[0],
    1315                 :             :                 Anum_pg_propgraph_element_label_pgelelid,
    1316                 :             :                 BTEqualStrategyNumber,
    1317                 :             :                 F_OIDEQ, ObjectIdGetDatum(elemoid));
    1318                 :          52 :     scan = systable_beginscan(rel, PropgraphElementLabelElementLabelIndexId,
    1319                 :             :                               true, NULL, 1, key);
    1320                 :             : 
    1321         [ +  + ]:         148 :     while (HeapTupleIsValid(labeltup = systable_getnext(scan)))
    1322                 :             :     {
    1323                 :         100 :         Form_pg_propgraph_element_label ele_label = (Form_pg_propgraph_element_label) GETSTRUCT(labeltup);
    1324                 :             : 
    1325                 :         100 :         HeapTuple   proptup = SearchSysCache2(PROPGRAPHLABELPROP,
    1326                 :             :                                               ObjectIdGetDatum(ele_label->oid), ObjectIdGetDatum(propoid));
    1327                 :             : 
    1328         [ +  + ]:         100 :         if (!proptup)
    1329                 :          96 :             continue;
    1330                 :           4 :         n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHLABELPROP,
    1331                 :             :                                                                     proptup, Anum_pg_propgraph_label_property_plpexpr)));
    1332                 :           4 :         ChangeVarNodes(n, 1, rtindex, 0);
    1333                 :             : 
    1334                 :           4 :         ReleaseSysCache(proptup);
    1335                 :           4 :         break;
    1336                 :             :     }
    1337                 :          52 :     systable_endscan(scan);
    1338                 :          52 :     table_close(rel, RowShareLock);
    1339                 :             : 
    1340                 :          52 :     return n;
    1341                 :             : }
        

Generated by: LCOV version 2.0-1