LCOV - code coverage report
Current view: top level - src/backend/rewrite - rewriteGraphTable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 97.7 % 396 387
Test Date: 2026-05-07 03:16:33 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit

            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          489 : rewriteGraphTable(Query *parsetree, int rt_index)
     111              : {
     112              :     RangeTblEntry *rte;
     113              :     Query      *graph_table_query;
     114              :     List       *path_pattern;
     115          489 :     List       *pathqueries = NIL;
     116              : 
     117          489 :     rte = rt_fetch(rt_index, parsetree->rtable);
     118              : 
     119              :     Assert(list_length(rte->graph_pattern->path_pattern_list) == 1);
     120              : 
     121          489 :     path_pattern = linitial(rte->graph_pattern->path_pattern_list);
     122          489 :     pathqueries = generate_queries_for_path_pattern(rte, path_pattern);
     123          449 :     graph_table_query = generate_union_from_pathqueries(&pathqueries);
     124              : 
     125          449 :     AcquireRewriteLocks(graph_table_query, true, false);
     126              : 
     127          449 :     rte->rtekind = RTE_SUBQUERY;
     128          449 :     rte->subquery = graph_table_query;
     129          449 :     rte->lateral = true;
     130              : 
     131              :     /*
     132              :      * Reset no longer applicable fields, to appease
     133              :      * WRITE_READ_PARSE_PLAN_TREES.
     134              :      */
     135          449 :     rte->graph_pattern = NULL;
     136          449 :     rte->graph_table_columns = NIL;
     137              : 
     138          449 :     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          489 : generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern)
     176              : {
     177          489 :     List       *pathqueries = NIL;
     178          489 :     List       *path_elem_lists = NIL;
     179          489 :     int         factorpos = 0;
     180          489 :     List       *path_factors = NIL;
     181          489 :     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         2459 :     foreach_node(GraphElementPattern, gep, path_pattern)
     193              :     {
     194         1505 :         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         4502 :         foreach_ptr(struct path_factor, other, path_factors)
     205              :         {
     206         1652 :             if (gep->variable && other->variable &&
     207         1094 :                 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         1497 :         if (!pf)
     249              :         {
     250         1353 :             pf = palloc0_object(struct path_factor);
     251         1353 :             pf->factorpos = factorpos++;
     252         1353 :             pf->kind = gep->kind;
     253         1353 :             pf->labelexpr = gep->labelexpr;
     254         1353 :             pf->variable = gep->variable;
     255         1353 :             pf->whereClause = gep->whereClause;
     256              : 
     257         1353 :             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         1497 :         if (prev_pf)
     285              :         {
     286         1008 :             if (prev_pf->kind == EDGE_PATTERN_RIGHT || prev_pf->kind == EDGE_PATTERN_ANY)
     287              :             {
     288              :                 Assert(!IS_EDGE_PATTERN(pf->kind));
     289          492 :                 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          492 :                 prev_pf->dest_pf = pf;
     294              :             }
     295          516 :             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         1008 :             if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY)
     311              :             {
     312              :                 Assert(!IS_EDGE_PATTERN(prev_pf->kind));
     313          500 :                 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          496 :                 pf->src_pf = prev_pf;
     318              :             }
     319          508 :             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         1493 :         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         2243 :     foreach_ptr(struct path_factor, pf, path_factors)
     343         1297 :         path_elem_lists = lappend(path_elem_lists,
     344         1305 :                                   get_path_elements_for_path_factor(rte->relid, pf));
     345              : 
     346          469 :     pathqueries = generate_queries_for_path_pattern_recurse(rte, pathqueries,
     347              :                                                             NIL, path_elem_lists, 0);
     348          449 :     if (!pathqueries)
     349            4 :         pathqueries = list_make1(generate_query_for_empty_path_pattern(rte));
     350              : 
     351          449 :     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        10211 : generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, List *cur_path, List *path_elem_lists, int elempos)
     362              : {
     363        10211 :     List       *path_elems = list_nth_node(List, path_elem_lists, elempos);
     364              : 
     365              :     /* Guard against stack overflow due to complex path patterns. */
     366        10211 :     check_stack_depth();
     367              : 
     368        53304 :     foreach_ptr(struct path_element, pe, path_elems)
     369              :     {
     370              :         /* Update current path being built with current element. */
     371        33010 :         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        33010 :         if (list_length(path_elem_lists) == list_length(cur_path))
     378              :         {
     379        23268 :             Query      *pathquery = generate_query_for_graph_path(rte, cur_path);
     380              : 
     381              :             Assert(elempos == list_length(path_elem_lists) - 1);
     382        23248 :             if (pathquery)
     383          828 :                 pathqueries = lappend(pathqueries, pathquery);
     384              :         }
     385              :         else
     386         9742 :             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        32946 :         cur_path = list_delete_last(cur_path);
     392              :     }
     393              : 
     394        10147 :     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        23268 : generate_query_for_graph_path(RangeTblEntry *rte, List *graph_path)
     420              : {
     421        23268 :     Query      *path_query = makeNode(Query);
     422        23268 :     List       *fromlist = NIL;
     423        23268 :     List       *qual_exprs = NIL;
     424              :     List       *vars;
     425              : 
     426        23268 :     path_query->commandType = CMD_SELECT;
     427              : 
     428        53412 :     foreach_ptr(struct path_element, pe, graph_path)
     429              :     {
     430        51716 :         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        51716 :         if (IS_EDGE_PATTERN(pf->kind))
     439              :         {
     440              :             struct path_element *src_pe;
     441              :             struct path_element *dest_pe;
     442        25496 :             Expr       *edge_qual = NULL;
     443              : 
     444              :             Assert(pf->src_pf && pf->dest_pf);
     445        25496 :             src_pe = list_nth(graph_path, pf->src_pf->factorpos);
     446        25496 :             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        25496 :             if (src_pe->elemoid == pe->srcvertexid &&
     453         8776 :                 dest_pe->elemoid == pe->destvertexid)
     454         3068 :                 edge_qual = makeBoolExpr(AND_EXPR,
     455         3068 :                                          list_concat(copyObject(pe->src_quals),
     456         3068 :                                                      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        25496 :             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        25496 :             if (edge_qual == NULL)
     493        22420 :                 return NULL;
     494              : 
     495         3076 :             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        29296 :         rel = table_open(pe->reloid, AccessShareLock);
     511        29296 :         pni = addRangeTableEntryForRelation(make_parsestate(NULL), rel, AccessShareLock,
     512              :                                             NULL, true, false);
     513        29296 :         table_close(rel, NoLock);
     514        29296 :         path_query->rtable = lappend(path_query->rtable, pni->p_rte);
     515        29296 :         path_query->rteperminfos = lappend(path_query->rteperminfos, pni->p_perminfo);
     516        29296 :         pni->p_rte->perminfoindex = list_length(path_query->rteperminfos);
     517        29296 :         rtr = makeNode(RangeTblRef);
     518        29296 :         rtr->rtindex = list_length(path_query->rtable);
     519        29296 :         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        29296 :         if (pf->whereClause)
     530              :         {
     531              :             Node       *tr;
     532              : 
     533         3712 :             tr = replace_property_refs(rte->relid, pf->whereClause, list_make1(pe));
     534              : 
     535         3712 :             qual_exprs = lappend(qual_exprs, tr);
     536              :         }
     537              :     }
     538              : 
     539          848 :     if (rte->graph_pattern->whereClause)
     540              :     {
     541          342 :         Node       *path_quals = replace_property_refs(rte->relid,
     542          342 :                                                        (Node *) rte->graph_pattern->whereClause,
     543              :                                                        graph_path);
     544              : 
     545          342 :         qual_exprs = lappend(qual_exprs, path_quals);
     546              :     }
     547              : 
     548         1592 :     path_query->jointree = makeFromExpr(fromlist,
     549          744 :                                         qual_exprs ? (Node *) makeBoolExpr(AND_EXPR, qual_exprs, -1) : NULL);
     550              : 
     551              :     /* Construct query targetlist from COLUMNS specification of GRAPH_TABLE. */
     552          848 :     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          828 :     vars = pull_vars_of_level((Node *) list_make2(qual_exprs, path_query->targetList), 0);
     563         9734 :     foreach_node(Var, var, vars)
     564              :     {
     565         8078 :         RTEPermissionInfo *perminfo = getRTEPermissionInfo(path_query->rteperminfos,
     566         8078 :                                                            rt_fetch(var->varno, path_query->rtable));
     567              : 
     568              :         /* Must offset the attnum to fit in a bitmapset */
     569         8078 :         perminfo->selectedCols = bms_add_member(perminfo->selectedCols,
     570         8078 :                                                 var->varattno - FirstLowInvalidHeapAttributeNumber);
     571              :     }
     572              : 
     573          828 :     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          449 : generate_union_from_pathqueries(List **pathqueries)
     621              : {
     622          449 :     List       *rtable = NIL;
     623          449 :     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          449 :     if (list_length(*pathqueries) == 1)
     636              :     {
     637          322 :         *pathqueries = NIL;
     638          322 :         return sampleQuery;
     639              :     }
     640              : 
     641          127 :     sostmt = castNode(SetOperationStmt,
     642              :                       generate_setop_from_pathqueries(*pathqueries, &rtable, NULL));
     643              : 
     644              :     /* Encapsulate the set operation statement into a Query. */
     645          127 :     union_query = makeNode(Query);
     646          127 :     union_query->commandType = CMD_SELECT;
     647          127 :     union_query->rtable = rtable;
     648          127 :     union_query->setOperations = (Node *) sostmt;
     649          127 :     union_query->rteperminfos = NIL;
     650          127 :     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          127 :     union_query->targetList = NULL;
     663          127 :     resno = 1;
     664          460 :     forfour(lct, sostmt->colTypes,
     665              :             lcm, sostmt->colTypmods,
     666              :             lcc, sostmt->colCollations,
     667              :             lctl, sampleQuery->targetList)
     668              :     {
     669          333 :         Oid         colType = lfirst_oid(lct);
     670          333 :         int32       colTypmod = lfirst_int(lcm);
     671          333 :         Oid         colCollation = lfirst_oid(lcc);
     672          333 :         TargetEntry *sample_tle = (TargetEntry *) lfirst(lctl);
     673              :         char       *colName;
     674              :         TargetEntry *tle;
     675              :         Var        *var;
     676              : 
     677              :         Assert(!sample_tle->resjunk);
     678          333 :         colName = pstrdup(sample_tle->resname);
     679          333 :         var = makeVar(1, sample_tle->resno, colType, colTypmod, colCollation, 0);
     680          333 :         var->location = exprLocation((Node *) sample_tle->expr);
     681          333 :         tle = makeTargetEntry((Expr *) var, (AttrNumber) resno++, colName, false);
     682          333 :         union_query->targetList = lappend(union_query->targetList, tle);
     683              :     }
     684              : 
     685          127 :     *pathqueries = NIL;
     686          127 :     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          637 : generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist)
     697              : {
     698              :     SetOperationStmt *sostmt;
     699              :     Query      *lquery;
     700              :     Node       *rarg;
     701          637 :     RangeTblRef *lrtr = makeNode(RangeTblRef);
     702              :     List       *rtargetlist;
     703              :     ParseNamespaceItem *pni;
     704              : 
     705              :     /* Guard against stack overflow due to many path queries. */
     706          637 :     check_stack_depth();
     707              : 
     708              :     /* Recursion termination condition. */
     709          637 :     if (list_length(pathqueries) == 0)
     710              :     {
     711          127 :         *targetlist = NIL;
     712          127 :         return NULL;
     713              :     }
     714              : 
     715          510 :     lquery = linitial_node(Query, pathqueries);
     716              : 
     717          510 :     pni = addRangeTableEntryForSubquery(make_parsestate(NULL), lquery, NULL,
     718              :                                         false, false);
     719          510 :     *rtable = lappend(*rtable, pni->p_rte);
     720          510 :     lrtr->rtindex = list_length(*rtable);
     721          510 :     rarg = generate_setop_from_pathqueries(list_delete_first(pathqueries), rtable, &rtargetlist);
     722          510 :     if (rarg == NULL)
     723              :     {
     724              :         /*
     725              :          * No further path queries in the list. Convert the last query into a
     726              :          * RangeTblRef as expected by SetOperationStmt. Extract a list of the
     727              :          * non-junk TLEs for upper-level processing.
     728              :          */
     729          127 :         if (targetlist)
     730              :         {
     731          127 :             *targetlist = NIL;
     732          587 :             foreach_node(TargetEntry, tle, lquery->targetList)
     733              :             {
     734          333 :                 if (!tle->resjunk)
     735          333 :                     *targetlist = lappend(*targetlist, tle);
     736              :             }
     737              :         }
     738          127 :         return (Node *) lrtr;
     739              :     }
     740              : 
     741          383 :     sostmt = makeNode(SetOperationStmt);
     742          383 :     sostmt->op = SETOP_UNION;
     743          383 :     sostmt->all = true;
     744          383 :     sostmt->larg = (Node *) lrtr;
     745          383 :     sostmt->rarg = rarg;
     746          383 :     constructSetOpTargetlist(NULL, sostmt, lquery->targetList, rtargetlist, targetlist, "UNION", false);
     747              : 
     748          383 :     return (Node *) sostmt;
     749              : }
     750              : 
     751              : /*
     752              :  * Construct a path_element object for the graph element given by `elemoid`
     753              :  * satisfied by the path factor `pf`.
     754              :  *
     755              :  * If the type of graph element does not fit the element pattern kind, the
     756              :  * function returns NULL.
     757              :  */
     758              : static struct path_element *
     759         4582 : create_pe_for_element(struct path_factor *pf, Oid elemoid)
     760              : {
     761         4582 :     HeapTuple   eletup = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(elemoid));
     762              :     Form_pg_propgraph_element pgeform;
     763              :     struct path_element *pe;
     764              : 
     765         4582 :     if (!eletup)
     766            0 :         elog(ERROR, "cache lookup failed for property graph element %u", elemoid);
     767         4582 :     pgeform = ((Form_pg_propgraph_element) GETSTRUCT(eletup));
     768              : 
     769         4582 :     if ((pgeform->pgekind == PGEKIND_VERTEX && pf->kind != VERTEX_PATTERN) ||
     770         3966 :         (pgeform->pgekind == PGEKIND_EDGE && !IS_EDGE_PATTERN(pf->kind)))
     771              :     {
     772         1924 :         ReleaseSysCache(eletup);
     773         1924 :         return NULL;
     774              :     }
     775              : 
     776         2658 :     pe = palloc0_object(struct path_element);
     777         2658 :     pe->path_factor = pf;
     778         2658 :     pe->elemoid = elemoid;
     779         2658 :     pe->reloid = pgeform->pgerelid;
     780              : 
     781              :     /*
     782              :      * When a path is converted into a query
     783              :      * (generate_query_for_graph_path()), a RangeTblEntry will be created for
     784              :      * every element in the path.  Fixing rtindexes of RangeTblEntrys here
     785              :      * makes it possible to craft elements' qual expressions only once while
     786              :      * we have access to the catalog entry. Otherwise they need to be crafted
     787              :      * as many times as the number of paths a given element appears in,
     788              :      * fetching catalog entry again each time.  Hence we simply assume
     789              :      * RangeTblEntrys will be created in the same order in which the
     790              :      * corresponding path factors appear in the list of path factors
     791              :      * representing a path pattern. That way their rtindexes will be same as
     792              :      * path_factor::factorpos + 1.
     793              :      */
     794         2658 :     if (IS_EDGE_PATTERN(pf->kind))
     795              :     {
     796         1228 :         pe->srcvertexid = pgeform->pgesrcvertexid;
     797         1228 :         pe->destvertexid = pgeform->pgedestvertexid;
     798              :         Assert(pf->src_pf && pf->dest_pf);
     799              : 
     800         1228 :         pe->src_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->src_pf->factorpos + 1,
     801              :                                                      pe->srcvertexid,
     802              :                                                      Anum_pg_propgraph_element_pgesrckey,
     803              :                                                      Anum_pg_propgraph_element_pgesrcref,
     804              :                                                      Anum_pg_propgraph_element_pgesrceqop);
     805         1228 :         pe->dest_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->dest_pf->factorpos + 1,
     806              :                                                       pe->destvertexid,
     807              :                                                       Anum_pg_propgraph_element_pgedestkey,
     808              :                                                       Anum_pg_propgraph_element_pgedestref,
     809              :                                                       Anum_pg_propgraph_element_pgedesteqop);
     810              :     }
     811              : 
     812         2658 :     ReleaseSysCache(eletup);
     813              : 
     814         2658 :     return pe;
     815              : }
     816              : 
     817              : /*
     818              :  * Returns the list of OIDs of graph labels which the given label expression
     819              :  * resolves to in the given property graph.
     820              :  */
     821              : static List *
     822         1305 : get_labels_for_expr(Oid propgraphid, Node *labelexpr)
     823              : {
     824              :     List       *label_oids;
     825              : 
     826         1305 :     if (!labelexpr)
     827              :     {
     828              :         Relation    rel;
     829              :         SysScanDesc scan;
     830              :         ScanKeyData key[1];
     831              :         HeapTuple   tup;
     832              : 
     833              :         /*
     834              :          * According to section 9.2 "Contextual inference of a set of labels"
     835              :          * subclause 2.a.ii of SQL/PGQ standard, element pattern which does
     836              :          * not have a label expression is considered to have label expression
     837              :          * equivalent to '%|!%' which is set of all labels.
     838              :          */
     839          408 :         label_oids = NIL;
     840          408 :         rel = table_open(PropgraphLabelRelationId, AccessShareLock);
     841          408 :         ScanKeyInit(&key[0],
     842              :                     Anum_pg_propgraph_label_pglpgid,
     843              :                     BTEqualStrategyNumber,
     844              :                     F_OIDEQ, ObjectIdGetDatum(propgraphid));
     845          408 :         scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId,
     846              :                                   true, NULL, 1, key);
     847         2728 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     848              :         {
     849         2320 :             Form_pg_propgraph_label label = (Form_pg_propgraph_label) GETSTRUCT(tup);
     850              : 
     851         2320 :             label_oids = lappend_oid(label_oids, label->oid);
     852              :         }
     853          408 :         systable_endscan(scan);
     854          408 :         table_close(rel, AccessShareLock);
     855              :     }
     856          897 :     else if (IsA(labelexpr, GraphLabelRef))
     857              :     {
     858          855 :         GraphLabelRef *glr = castNode(GraphLabelRef, labelexpr);
     859              : 
     860          855 :         label_oids = list_make1_oid(glr->labelid);
     861              :     }
     862           42 :     else if (IsA(labelexpr, BoolExpr))
     863              :     {
     864           42 :         BoolExpr   *be = castNode(BoolExpr, labelexpr);
     865           42 :         List       *label_exprs = be->args;
     866              : 
     867           42 :         label_oids = NIL;
     868          172 :         foreach_node(GraphLabelRef, glr, label_exprs)
     869           88 :             label_oids = lappend_oid(label_oids, glr->labelid);
     870              :     }
     871              :     else
     872              :     {
     873              :         /*
     874              :          * should not reach here since gram.y will not generate a label
     875              :          * expression with other node types.
     876              :          */
     877            0 :         elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
     878              :     }
     879              : 
     880         1305 :     return label_oids;
     881              : }
     882              : 
     883              : /*
     884              :  * Return a list of all the graph elements that satisfy the graph element pattern
     885              :  * represented by the given path_factor `pf`.
     886              :  *
     887              :  * First we find all the graph labels that satisfy the label expression in path
     888              :  * factor. Each label is associated with one or more graph elements.  A union of
     889              :  * all such elements satisfies the element pattern. We create one path_element
     890              :  * object representing every element whose graph element kind qualifies the
     891              :  * element pattern kind. A list of all such path_element objects is returned.
     892              :  *
     893              :  * Note that we need to report an error for an explicitly specified label which
     894              :  * is not associated with any graph element of the required kind. So we have to
     895              :  * treat each label separately. Without that requirement we could have collected
     896              :  * all the unique elements first and then created path_element objects for them
     897              :  * to simplify the code.
     898              :  */
     899              : static List *
     900         1305 : get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf)
     901              : {
     902         1305 :     List       *label_oids = get_labels_for_expr(propgraphid, pf->labelexpr);
     903         1305 :     List       *elem_oids_seen = NIL;
     904         1305 :     List       *pf_elem_oids = NIL;
     905         1305 :     List       *path_elements = NIL;
     906         1305 :     List       *unresolved_labels = NIL;
     907              :     Relation    rel;
     908              :     SysScanDesc scan;
     909              :     ScanKeyData key[1];
     910              :     HeapTuple   tup;
     911              : 
     912              :     /*
     913              :      * A property graph element can be either a vertex or an edge. Other types
     914              :      * of path factors like nested path pattern need to be handled separately
     915              :      * when supported.
     916              :      */
     917              :     Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
     918              : 
     919         1305 :     rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
     920         5853 :     foreach_oid(labeloid, label_oids)
     921              :     {
     922         3259 :         bool        found = false;
     923              : 
     924         3259 :         ScanKeyInit(&key[0],
     925              :                     Anum_pg_propgraph_element_label_pgellabelid,
     926              :                     BTEqualStrategyNumber,
     927              :                     F_OIDEQ, ObjectIdGetDatum(labeloid));
     928         3259 :         scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true,
     929              :                                   NULL, 1, key);
     930        10919 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     931              :         {
     932         7660 :             Form_pg_propgraph_element_label label_elem = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
     933         7660 :             Oid         elem_oid = label_elem->pgelelid;
     934              : 
     935         7660 :             if (!list_member_oid(elem_oids_seen, elem_oid))
     936              :             {
     937              :                 /*
     938              :                  * Create path_element object if the new element qualifies the
     939              :                  * element pattern kind.
     940              :                  */
     941         4582 :                 struct path_element *pe = create_pe_for_element(pf, elem_oid);
     942              : 
     943         4582 :                 if (pe)
     944              :                 {
     945         2658 :                     path_elements = lappend(path_elements, pe);
     946              : 
     947              :                     /* Remember qualified elements. */
     948         2658 :                     pf_elem_oids = lappend_oid(pf_elem_oids, elem_oid);
     949         2658 :                     found = true;
     950              :                 }
     951              : 
     952              :                 /*
     953              :                  * Remember qualified and unqualified elements processed so
     954              :                  * far to avoid processing already processed elements again.
     955              :                  */
     956         4582 :                 elem_oids_seen = lappend_oid(elem_oids_seen, label_elem->pgelelid);
     957              :             }
     958         3078 :             else if (list_member_oid(pf_elem_oids, elem_oid))
     959              :             {
     960              :                 /*
     961              :                  * The graph element is known to qualify the given element
     962              :                  * pattern. Flag that the current label has at least one
     963              :                  * qualified element associated with it.
     964              :                  */
     965         1582 :                 found = true;
     966              :             }
     967              :         }
     968              : 
     969         3259 :         if (!found)
     970              :         {
     971              :             /*
     972              :              * We did not find any qualified element associated with this
     973              :              * label. The label or its properties can not be associated with
     974              :              * the given element pattern. Throw an error if the label was
     975              :              * explicitly specified in the element pattern. Otherwise remember
     976              :              * it for later use.
     977              :              */
     978          984 :             if (!pf->labelexpr)
     979          976 :                 unresolved_labels = lappend_oid(unresolved_labels, labeloid);
     980              :             else
     981            8 :                 ereport(ERROR,
     982              :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     983              :                          errmsg("no property graph element of type \"%s\" has label \"%s\" associated with it in property graph \"%s\"",
     984              :                                 pf->kind == VERTEX_PATTERN ? "vertex" : "edge",
     985              :                                 get_propgraph_label_name(labeloid),
     986              :                                 get_rel_name(propgraphid))));
     987              :         }
     988              : 
     989         3251 :         systable_endscan(scan);
     990              :     }
     991         1297 :     table_close(rel, AccessShareLock);
     992              : 
     993              :     /*
     994              :      * Remove the labels which were not explicitly mentioned in the label
     995              :      * expression but do not have any qualified elements associated with them.
     996              :      * Properties associated with such labels may not be referenced. See
     997              :      * replace_property_refs_mutator() for more details.
     998              :      */
     999         1297 :     pf->labeloids = list_difference_oid(label_oids, unresolved_labels);
    1000              : 
    1001         1297 :     return path_elements;
    1002              : }
    1003              : 
    1004              : /*
    1005              :  * Mutating property references into table variables
    1006              :  */
    1007              : 
    1008              : struct replace_property_refs_context
    1009              : {
    1010              :     Oid         propgraphid;
    1011              :     const List *mappings;
    1012              : };
    1013              : 
    1014              : static Node *
    1015        45214 : replace_property_refs_mutator(Node *node, struct replace_property_refs_context *context)
    1016              : {
    1017        45214 :     if (node == NULL)
    1018            0 :         return NULL;
    1019        45214 :     if (IsA(node, Var))
    1020              :     {
    1021            8 :         Var        *var = (Var *) node;
    1022            8 :         Var        *newvar = copyObject(var);
    1023              : 
    1024              :         /*
    1025              :          * If it's already a Var, then it was a lateral reference.  Since we
    1026              :          * are in a subquery after the rewrite, we have to increase the level
    1027              :          * by one.
    1028              :          */
    1029            8 :         newvar->varlevelsup++;
    1030              : 
    1031            8 :         return (Node *) newvar;
    1032              :     }
    1033        45206 :     else if (IsA(node, GraphPropertyRef))
    1034              :     {
    1035        10890 :         GraphPropertyRef *gpr = (GraphPropertyRef *) node;
    1036        10890 :         Node       *n = NULL;
    1037        10890 :         struct path_element *found_mapping = NULL;
    1038        10890 :         struct path_factor *mapping_factor = NULL;
    1039        10890 :         List       *unrelated_labels = NIL;
    1040              : 
    1041        25016 :         foreach_ptr(struct path_element, m, context->mappings)
    1042              :         {
    1043        14126 :             if (m->path_factor->variable && strcmp(gpr->elvarname, m->path_factor->variable) == 0)
    1044              :             {
    1045        10890 :                 found_mapping = m;
    1046        10890 :                 break;
    1047              :             }
    1048              :         }
    1049              : 
    1050              :         /*
    1051              :          * transformGraphTablePropertyRef() would not create a
    1052              :          * GraphPropertyRef for a variable which is not present in the graph
    1053              :          * path pattern.
    1054              :          */
    1055              :         Assert(found_mapping);
    1056              : 
    1057        10890 :         mapping_factor = found_mapping->path_factor;
    1058              : 
    1059              :         /*
    1060              :          * Find property definition for given element through any of the
    1061              :          * associated labels qualifying the given element pattern.
    1062              :          */
    1063        59710 :         foreach_oid(labeloid, mapping_factor->labeloids)
    1064              :         {
    1065        37930 :             Oid         elem_labelid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
    1066              :                                                        Anum_pg_propgraph_element_label_oid,
    1067              :                                                        ObjectIdGetDatum(found_mapping->elemoid),
    1068              :                                                        ObjectIdGetDatum(labeloid));
    1069              : 
    1070        37930 :             if (OidIsValid(elem_labelid))
    1071              :             {
    1072        22950 :                 HeapTuple   tup = SearchSysCache2(PROPGRAPHLABELPROP, ObjectIdGetDatum(elem_labelid),
    1073              :                                                   ObjectIdGetDatum(gpr->propid));
    1074              : 
    1075        22950 :                 if (!tup)
    1076              :                 {
    1077              :                     /*
    1078              :                      * The label is associated with the given element but it
    1079              :                      * is not associated with the required property. Check
    1080              :                      * next label.
    1081              :                      */
    1082        11720 :                     continue;
    1083              :                 }
    1084              : 
    1085        11230 :                 n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHLABELPROP,
    1086              :                                                                             tup, Anum_pg_propgraph_label_property_plpexpr)));
    1087        11230 :                 ChangeVarNodes(n, 1, mapping_factor->factorpos + 1, 0);
    1088              : 
    1089        11230 :                 ReleaseSysCache(tup);
    1090              :             }
    1091              :             else
    1092              :             {
    1093              :                 /*
    1094              :                  * Label is not associated with the element but it may be
    1095              :                  * associated with the property through some other element.
    1096              :                  * Save it for later use.
    1097              :                  */
    1098        14980 :                 unrelated_labels = lappend_oid(unrelated_labels, labeloid);
    1099              :             }
    1100              :         }
    1101              : 
    1102              :         /* See if we can resolve the property in some other way. */
    1103        10890 :         if (!n)
    1104              :         {
    1105           72 :             bool        prop_associated = false;
    1106              : 
    1107          168 :             foreach_oid(loid, unrelated_labels)
    1108              :             {
    1109           76 :                 if (is_property_associated_with_label(loid, gpr->propid))
    1110              :                 {
    1111           52 :                     prop_associated = true;
    1112           52 :                     break;
    1113              :                 }
    1114              :             }
    1115              : 
    1116           72 :             if (prop_associated)
    1117              :             {
    1118              :                 /*
    1119              :                  * The property is associated with at least one of the labels
    1120              :                  * that satisfy given element pattern. If it's associated with
    1121              :                  * the given element (through some other label), use
    1122              :                  * corresponding value expression. Otherwise NULL. Ref.
    1123              :                  * SQL/PGQ standard section 6.5 Property Reference, General
    1124              :                  * Rule 2.b.
    1125              :                  */
    1126           52 :                 n = get_element_property_expr(found_mapping->elemoid, gpr->propid,
    1127           52 :                                               mapping_factor->factorpos + 1);
    1128              : 
    1129           52 :                 if (!n)
    1130           48 :                     n = (Node *) makeNullConst(gpr->typeId, gpr->typmod, gpr->collation);
    1131              :             }
    1132              : 
    1133              :         }
    1134              : 
    1135        10890 :         if (!n)
    1136           20 :             ereport(ERROR,
    1137              :                     errcode(ERRCODE_UNDEFINED_OBJECT),
    1138              :                     errmsg("property \"%s\" for element variable \"%s\" not found",
    1139              :                            get_propgraph_property_name(gpr->propid), mapping_factor->variable));
    1140              : 
    1141        10870 :         return n;
    1142              :     }
    1143              : 
    1144        34316 :     return expression_tree_mutator(node, replace_property_refs_mutator, context);
    1145              : }
    1146              : 
    1147              : static Node *
    1148         4902 : replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
    1149              : {
    1150              :     struct replace_property_refs_context context;
    1151              : 
    1152         4902 :     context.mappings = mappings;
    1153         4902 :     context.propgraphid = propgraphid;
    1154              : 
    1155         4902 :     return expression_tree_mutator(node, replace_property_refs_mutator, &context);
    1156              : }
    1157              : 
    1158              : /*
    1159              :  * Build join qualification expressions between edge and vertex tables.
    1160              :  */
    1161              : static List *
    1162         2456 : 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)
    1163              : {
    1164         2456 :     List       *quals = NIL;
    1165              :     Form_pg_propgraph_element pgeform;
    1166              :     Datum       datum;
    1167              :     Datum      *d1,
    1168              :                *d2,
    1169              :                *d3;
    1170              :     int         n1,
    1171              :                 n2,
    1172              :                 n3;
    1173         2456 :     ParseState *pstate = make_parsestate(NULL);
    1174         2456 :     Oid         refrelid = GetSysCacheOid1(PROPGRAPHELOID, Anum_pg_propgraph_element_pgerelid, ObjectIdGetDatum(refid));
    1175              : 
    1176         2456 :     pgeform = (Form_pg_propgraph_element) GETSTRUCT(edgetup);
    1177              : 
    1178         2456 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_key_attnum);
    1179         2456 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d1, NULL, &n1);
    1180              : 
    1181         2456 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_ref_attnum);
    1182         2456 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d2, NULL, &n2);
    1183              : 
    1184         2456 :     datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_eqop_attnum);
    1185         2456 :     deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &d3, NULL, &n3);
    1186              : 
    1187         2456 :     if (n1 != n2)
    1188            0 :         elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
    1189         2456 :     if (n1 != n3)
    1190            0 :         elog(ERROR, "array size key (%d) vs operator (%d) mismatch for element ID %u", catalog_key_attnum, catalog_eqop_attnum, pgeform->oid);
    1191              : 
    1192         5576 :     for (int i = 0; i < n1; i++)
    1193              :     {
    1194         3120 :         AttrNumber  keyattn = DatumGetInt16(d1[i]);
    1195         3120 :         AttrNumber  refattn = DatumGetInt16(d2[i]);
    1196         3120 :         Oid         eqop = DatumGetObjectId(d3[i]);
    1197              :         Var        *keyvar;
    1198              :         Var        *refvar;
    1199              :         Oid         atttypid;
    1200              :         int32       atttypmod;
    1201              :         Oid         attcoll;
    1202              :         HeapTuple   tup;
    1203              :         Form_pg_operator opform;
    1204              :         List       *args;
    1205              :         Oid         actual_arg_types[2];
    1206              :         Oid         declared_arg_types[2];
    1207              :         OpExpr     *linkqual;
    1208              : 
    1209         3120 :         get_atttypetypmodcoll(pgeform->pgerelid, keyattn, &atttypid, &atttypmod, &attcoll);
    1210         3120 :         keyvar = makeVar(edgerti, keyattn, atttypid, atttypmod, attcoll, 0);
    1211         3120 :         get_atttypetypmodcoll(refrelid, refattn, &atttypid, &atttypmod, &attcoll);
    1212         3120 :         refvar = makeVar(refrti, refattn, atttypid, atttypmod, attcoll, 0);
    1213              : 
    1214         3120 :         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(eqop));
    1215         3120 :         if (!HeapTupleIsValid(tup))
    1216            0 :             elog(ERROR, "cache lookup failed for operator %u", eqop);
    1217         3120 :         opform = (Form_pg_operator) GETSTRUCT(tup);
    1218              :         /* An equality operator is a binary operator returning boolean result. */
    1219              :         Assert(opform->oprkind == 'b'
    1220              :                && RegProcedureIsValid(opform->oprcode)
    1221              :                && opform->oprresult == BOOLOID
    1222              :                && !get_func_retset(opform->oprcode));
    1223              : 
    1224              :         /*
    1225              :          * Prepare operands and cast them to the types required by the
    1226              :          * equality operator. Similar to PK/FK quals, referenced vertex key is
    1227              :          * used as left operand and referencing edge key is used as right
    1228              :          * operand.
    1229              :          */
    1230         3120 :         args = list_make2(refvar, keyvar);
    1231         3120 :         actual_arg_types[0] = exprType((Node *) refvar);
    1232         3120 :         actual_arg_types[1] = exprType((Node *) keyvar);
    1233         3120 :         declared_arg_types[0] = opform->oprleft;
    1234         3120 :         declared_arg_types[1] = opform->oprright;
    1235         3120 :         make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
    1236              : 
    1237         3120 :         linkqual = makeNode(OpExpr);
    1238         3120 :         linkqual->opno = opform->oid;
    1239         3120 :         linkqual->opfuncid = opform->oprcode;
    1240         3120 :         linkqual->opresulttype = opform->oprresult;
    1241         3120 :         linkqual->opretset = false;
    1242              :         /* opcollid and inputcollid will be set by parse_collate.c */
    1243         3120 :         linkqual->args = args;
    1244         3120 :         linkqual->location = -1;
    1245              : 
    1246         3120 :         ReleaseSysCache(tup);
    1247         3120 :         quals = lappend(quals, linkqual);
    1248              :     }
    1249              : 
    1250         2456 :     assign_expr_collations(pstate, (Node *) quals);
    1251              : 
    1252         2456 :     return quals;
    1253              : }
    1254              : 
    1255              : /*
    1256              :  * Check if the given property is associated with the given label.
    1257              :  *
    1258              :  * A label projects the same set of properties through every element it is
    1259              :  * associated with. Find any of the elements and return true if that element is
    1260              :  * associated with the given property. False otherwise.
    1261              :  */
    1262              : static bool
    1263           76 : is_property_associated_with_label(Oid labeloid, Oid propoid)
    1264              : {
    1265              :     Relation    rel;
    1266              :     SysScanDesc scan;
    1267              :     ScanKeyData key[1];
    1268              :     HeapTuple   tup;
    1269           76 :     bool        associated = false;
    1270              : 
    1271           76 :     rel = table_open(PropgraphElementLabelRelationId, RowShareLock);
    1272           76 :     ScanKeyInit(&key[0],
    1273              :                 Anum_pg_propgraph_element_label_pgellabelid,
    1274              :                 BTEqualStrategyNumber,
    1275              :                 F_OIDEQ, ObjectIdGetDatum(labeloid));
    1276           76 :     scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId,
    1277              :                               true, NULL, 1, key);
    1278              : 
    1279           76 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
    1280              :     {
    1281           76 :         Form_pg_propgraph_element_label ele_label = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
    1282              : 
    1283           76 :         associated = SearchSysCacheExists2(PROPGRAPHLABELPROP,
    1284              :                                            ObjectIdGetDatum(ele_label->oid), ObjectIdGetDatum(propoid));
    1285              :     }
    1286           76 :     systable_endscan(scan);
    1287           76 :     table_close(rel, RowShareLock);
    1288              : 
    1289           76 :     return associated;
    1290              : }
    1291              : 
    1292              : /*
    1293              :  * If given element has the given property associated with it, through any of
    1294              :  * the associated labels, return value expression of the property. Otherwise
    1295              :  * NULL.
    1296              :  */
    1297              : static Node *
    1298           52 : get_element_property_expr(Oid elemoid, Oid propoid, int rtindex)
    1299              : {
    1300              :     Relation    rel;
    1301              :     SysScanDesc scan;
    1302              :     ScanKeyData key[1];
    1303              :     HeapTuple   labeltup;
    1304           52 :     Node       *n = NULL;
    1305              : 
    1306           52 :     rel = table_open(PropgraphElementLabelRelationId, RowShareLock);
    1307           52 :     ScanKeyInit(&key[0],
    1308              :                 Anum_pg_propgraph_element_label_pgelelid,
    1309              :                 BTEqualStrategyNumber,
    1310              :                 F_OIDEQ, ObjectIdGetDatum(elemoid));
    1311           52 :     scan = systable_beginscan(rel, PropgraphElementLabelElementLabelIndexId,
    1312              :                               true, NULL, 1, key);
    1313              : 
    1314          148 :     while (HeapTupleIsValid(labeltup = systable_getnext(scan)))
    1315              :     {
    1316          100 :         Form_pg_propgraph_element_label ele_label = (Form_pg_propgraph_element_label) GETSTRUCT(labeltup);
    1317              : 
    1318          100 :         HeapTuple   proptup = SearchSysCache2(PROPGRAPHLABELPROP,
    1319              :                                               ObjectIdGetDatum(ele_label->oid), ObjectIdGetDatum(propoid));
    1320              : 
    1321          100 :         if (!proptup)
    1322           96 :             continue;
    1323            4 :         n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHLABELPROP,
    1324              :                                                                     proptup, Anum_pg_propgraph_label_property_plpexpr)));
    1325            4 :         ChangeVarNodes(n, 1, rtindex, 0);
    1326              : 
    1327            4 :         ReleaseSysCache(proptup);
    1328            4 :         break;
    1329              :     }
    1330           52 :     systable_endscan(scan);
    1331           52 :     table_close(rel, RowShareLock);
    1332              : 
    1333           52 :     return n;
    1334              : }
        

Generated by: LCOV version 2.0-1