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

Generated by: LCOV version 2.0-1