LCOV - differential code coverage report
Current view: top level - src/backend/rewrite - rewriteGraphTable.c (source / functions) Coverage Total Hit UNC GNC
Current: bed3ffbf9d952be6c7d739d068cdce44c046dfb7 vs 574581b50ac9c63dd9e4abebb731a3b67e5b50f6 Lines: 97.9 % 422 413 9 413
Current Date: 2026-05-05 10:23:31 +0900 Functions: 100.0 % 15 15 15
Baseline: lcov-20260505-025707-baseline Branches: 76.2 % 408 311 97 311
Baseline Date: 2026-05-05 10:27:06 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 2 2 2
(30,360] days: 97.9 % 420 411 9 411
Function coverage date bins:
(30,360] days: 100.0 % 15 15 15
Branch coverage date bins:
(30,360] days: 76.2 % 408 311 97 311

 Age         Owner                    Branch data    TLA  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 *
   50 peter@eisentraut.org      110                 :GNC         485 : rewriteGraphTable(Query *parsetree, int rt_index)
                                111                 :                : {
                                112                 :                :     RangeTblEntry *rte;
                                113                 :                :     Query      *graph_table_query;
                                114                 :                :     List       *path_pattern;
                                115                 :            485 :     List       *pathqueries = NIL;
                                116                 :                : 
                                117                 :            485 :     rte = rt_fetch(rt_index, parsetree->rtable);
                                118                 :                : 
                                119         [ -  + ]:            485 :     Assert(list_length(rte->graph_pattern->path_pattern_list) == 1);
                                120                 :                : 
                                121                 :            485 :     path_pattern = linitial(rte->graph_pattern->path_pattern_list);
                                122                 :            485 :     pathqueries = generate_queries_for_path_pattern(rte, path_pattern);
                                123                 :            445 :     graph_table_query = generate_union_from_pathqueries(&pathqueries);
                                124                 :                : 
                                125                 :            445 :     AcquireRewriteLocks(graph_table_query, true, false);
                                126                 :                : 
                                127                 :            445 :     rte->rtekind = RTE_SUBQUERY;
                                128                 :            445 :     rte->subquery = graph_table_query;
                                129                 :            445 :     rte->lateral = true;
                                130                 :                : 
                                131                 :                :     /*
                                132                 :                :      * Reset no longer applicable fields, to appease
                                133                 :                :      * WRITE_READ_PARSE_PLAN_TREES.
                                134                 :                :      */
                                135                 :            445 :     rte->graph_pattern = NULL;
                                136                 :            445 :     rte->graph_table_columns = NIL;
                                137                 :                : 
                                138                 :            445 :     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 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                 :            485 : generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern)
                                176                 :                : {
                                177                 :            485 :     List       *pathqueries = NIL;
                                178                 :            485 :     List       *path_elem_lists = NIL;
                                179                 :            485 :     int         factorpos = 0;
                                180                 :            485 :     List       *path_factors = NIL;
                                181                 :            485 :     struct path_factor *prev_pf = NULL;
                                182                 :                : 
                                183         [ -  + ]:            485 :     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   [ +  -  +  +  :           2443 :     foreach_node(GraphElementPattern, gep, path_pattern)
                                              +  + ]
                                193                 :                :     {
                                194                 :           1497 :         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   [ +  +  +  +  :           1497 :         Assert(gep->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(gep->kind));
                                        +  +  -  + ]
                                202         [ -  + ]:           1497 :         Assert(!gep->quantifier);
                                203                 :                : 
                                204   [ +  +  +  +  :           4484 :         foreach_ptr(struct path_factor, other, path_factors)
                                              +  + ]
                                205                 :                :         {
                                206   [ +  +  +  + ]:           1650 :             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         [ +  + ]:           1489 :         if (!pf)
                                249                 :                :         {
   41                           250                 :           1345 :             pf = palloc0_object(struct path_factor);
                                251                 :           1345 :             pf->factorpos = factorpos++;
                                252                 :           1345 :             pf->kind = gep->kind;
                                253                 :           1345 :             pf->labelexpr = gep->labelexpr;
                                254                 :           1345 :             pf->variable = gep->variable;
                                255                 :           1345 :             pf->whereClause = gep->whereClause;
                                256                 :                : 
                                257                 :           1345 :             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 vertexes. 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                 :                :          */
   50                           284         [ +  + ]:           1489 :         if (prev_pf)
                                285                 :                :         {
                                286   [ +  +  +  + ]:           1004 :             if (prev_pf->kind == EDGE_PATTERN_RIGHT || prev_pf->kind == EDGE_PATTERN_ANY)
                                287                 :                :             {
                                288   [ +  -  +  -  :            490 :                 Assert(!IS_EDGE_PATTERN(pf->kind));
                                              -  + ]
                                289   [ +  +  -  + ]:            490 :                 if (prev_pf->dest_pf && prev_pf->dest_pf != pf)
   50 peter@eisentraut.org      290         [ #  # ]:UNC           0 :                     ereport(ERROR,
                                291                 :                :                             errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                292                 :                :                             errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
   50 peter@eisentraut.org      293                 :GNC         490 :                 prev_pf->dest_pf = pf;
                                294                 :                :             }
                                295         [ +  + ]:            514 :             else if (prev_pf->kind == EDGE_PATTERN_LEFT)
                                296                 :                :             {
                                297   [ +  -  +  -  :              8 :                 Assert(!IS_EDGE_PATTERN(pf->kind));
                                              -  + ]
                                298   [ -  +  -  - ]:              8 :                 if (prev_pf->src_pf && prev_pf->src_pf != pf)
   50 peter@eisentraut.org      299         [ #  # ]:UNC           0 :                     ereport(ERROR,
                                300                 :                :                             errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                301                 :                :                             errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
   50 peter@eisentraut.org      302                 :GNC           8 :                 prev_pf->src_pf = pf;
                                303                 :                :             }
                                304                 :                :             else
                                305                 :                :             {
   39                           306         [ -  + ]:            506 :                 Assert(prev_pf->kind == VERTEX_PATTERN);
                                307   [ +  +  +  +  :            506 :                 Assert(IS_EDGE_PATTERN(pf->kind));
                                              -  + ]
                                308                 :                :             }
                                309                 :                : 
   50                           310   [ +  +  +  + ]:           1004 :             if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY)
                                311                 :                :             {
                                312   [ +  -  +  -  :            498 :                 Assert(!IS_EDGE_PATTERN(prev_pf->kind));
                                              -  + ]
                                313   [ +  +  +  + ]:            498 :                 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 vertexes even in a cyclic pattern"));
                                317                 :            494 :                 pf->src_pf = prev_pf;
                                318                 :                :             }
                                319         [ +  + ]:            506 :             else if (pf->kind == EDGE_PATTERN_LEFT)
                                320                 :                :             {
                                321   [ +  -  +  -  :              8 :                 Assert(!IS_EDGE_PATTERN(prev_pf->kind));
                                              -  + ]
                                322   [ -  +  -  - ]:              8 :                 if (pf->dest_pf && pf->dest_pf != prev_pf)
   50 peter@eisentraut.org      323         [ #  # ]:UNC           0 :                     ereport(ERROR,
                                324                 :                :                             errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                325                 :                :                             errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
   50 peter@eisentraut.org      326                 :GNC           8 :                 pf->dest_pf = prev_pf;
                                327                 :                :             }
                                328                 :                :             else
                                329                 :                :             {
   39                           330         [ -  + ]:            498 :                 Assert(pf->kind == VERTEX_PATTERN);
                                331   [ +  +  +  +  :            498 :                 Assert(IS_EDGE_PATTERN(prev_pf->kind));
                                              -  + ]
                                332                 :                :             }
                                333                 :                :         }
                                334                 :                : 
   50                           335                 :           1485 :         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   [ +  -  +  +  :           2227 :     foreach_ptr(struct path_factor, pf, path_factors)
                                              +  + ]
                                343                 :           1289 :         path_elem_lists = lappend(path_elem_lists,
                                344                 :           1297 :                                   get_path_elements_for_path_factor(rte->relid, pf));
                                345                 :                : 
                                346                 :            465 :     pathqueries = generate_queries_for_path_pattern_recurse(rte, pathqueries,
                                347                 :                :                                                             NIL, path_elem_lists, 0);
                                348         [ +  + ]:            445 :     if (!pathqueries)
                                349                 :              4 :         pathqueries = list_make1(generate_query_for_empty_path_pattern(rte));
                                350                 :                : 
                                351                 :            445 :     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                 :          10172 : generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, List *cur_path, List *path_elem_lists, int elempos)
                                362                 :                : {
                                363                 :          10172 :     List       *path_elems = list_nth_node(List, path_elem_lists, elempos);
                                364                 :                : 
                                365                 :                :     /* Guard against stack overflow due to complex path patterns. */
   11                           366                 :          10172 :     check_stack_depth();
                                367                 :                : 
   50                           368   [ +  -  +  +  :          53024 :     foreach_ptr(struct path_element, pe, path_elems)
                                              +  + ]
                                369                 :                :     {
                                370                 :                :         /* Update current path being built with current element. */
                                371                 :          32808 :         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         [ +  + ]:          32808 :         if (list_length(path_elem_lists) == list_length(cur_path))
                                378                 :                :         {
                                379                 :          23101 :             Query      *pathquery = generate_query_for_graph_path(rte, cur_path);
                                380                 :                : 
                                381         [ -  + ]:          23081 :             Assert(elempos == list_length(path_elem_lists) - 1);
                                382         [ +  + ]:          23081 :             if (pathquery)
                                383                 :            819 :                 pathqueries = lappend(pathqueries, pathquery);
                                384                 :                :         }
                                385                 :                :         else
                                386                 :           9707 :             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                 :          32744 :         cur_path = list_delete_last(cur_path);
                                392                 :                :     }
                                393                 :                : 
                                394                 :          10108 :     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                 :          23101 : generate_query_for_graph_path(RangeTblEntry *rte, List *graph_path)
                                420                 :                : {
                                421                 :          23101 :     Query      *path_query = makeNode(Query);
                                422                 :          23101 :     List       *fromlist = NIL;
                                423                 :          23101 :     List       *qual_exprs = NIL;
                                424                 :                :     List       *vars;
                                425                 :                : 
                                426                 :          23101 :     path_query->commandType = CMD_SELECT;
                                427                 :                : 
                                428   [ +  -  +  +  :          53017 :     foreach_ptr(struct path_element, pe, graph_path)
                                              +  + ]
                                429                 :                :     {
                                430                 :          51339 :         struct path_factor *pf = pe->path_factor;
                                431                 :                :         RangeTblRef *rtr;
                                432                 :                :         Relation    rel;
                                433                 :                :         ParseNamespaceItem *pni;
                                434                 :                : 
                                435   [ +  +  +  +  :          51339 :         Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
                                        +  +  -  + ]
                                436                 :                : 
                                437                 :                :         /* Add conditions representing edge connections. */
                                438   [ +  +  +  +  :          51339 :         if (IS_EDGE_PATTERN(pf->kind))
                                              +  + ]
                                439                 :           3050 :         {
                                440                 :                :             struct path_element *src_pe;
                                441                 :                :             struct path_element *dest_pe;
                                442                 :          25312 :             Expr       *edge_qual = NULL;
                                443                 :                : 
                                444   [ +  -  -  + ]:          25312 :             Assert(pf->src_pf && pf->dest_pf);
                                445                 :          25312 :             src_pe = list_nth(graph_path, pf->src_pf->factorpos);
                                446                 :          25312 :             dest_pe = list_nth(graph_path, pf->dest_pf->factorpos);
                                447                 :                : 
                                448                 :                :             /* Make sure that the links of adjacent vertices are correct. */
                                449   [ +  -  -  + ]:          25312 :             Assert(pf->src_pf == src_pe->path_factor &&
                                450                 :                :                    pf->dest_pf == dest_pe->path_factor);
                                451                 :                : 
                                452         [ +  + ]:          25312 :             if (src_pe->elemoid == pe->srcvertexid &&
                                453         [ +  + ]:           8706 :                 dest_pe->elemoid == pe->destvertexid)
                                454                 :           3042 :                 edge_qual = makeBoolExpr(AND_EXPR,
                                455                 :           3042 :                                          list_concat(copyObject(pe->src_quals),
                                456                 :           3042 :                                                      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         [ +  + ]:          25312 :             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         [ +  + ]:          25312 :             if (edge_qual == NULL)
                                493                 :          22262 :                 return NULL;
                                494                 :                : 
                                495                 :           3050 :             qual_exprs = lappend(qual_exprs, edge_qual);
                                496                 :                :         }
                                497                 :                :         else
                                498   [ +  -  -  + ]:          26027 :             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                 :          29077 :         rel = table_open(pe->reloid, AccessShareLock);
                                511                 :          29077 :         pni = addRangeTableEntryForRelation(make_parsestate(NULL), rel, AccessShareLock,
                                512                 :                :                                             NULL, true, false);
                                513                 :          29077 :         table_close(rel, NoLock);
                                514                 :          29077 :         path_query->rtable = lappend(path_query->rtable, pni->p_rte);
                                515                 :          29077 :         path_query->rteperminfos = lappend(path_query->rteperminfos, pni->p_perminfo);
                                516                 :          29077 :         pni->p_rte->perminfoindex = list_length(path_query->rteperminfos);
                                517                 :          29077 :         rtr = makeNode(RangeTblRef);
                                518                 :          29077 :         rtr->rtindex = list_length(path_query->rtable);
                                519                 :          29077 :         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         [ -  + ]:          29077 :         Assert(pf->factorpos + 1 == rtr->rtindex);
                                528                 :                : 
                                529         [ +  + ]:          29077 :         if (pf->whereClause)
                                530                 :                :         {
                                531                 :                :             Node       *tr;
                                532                 :                : 
                                533                 :           3708 :             tr = replace_property_refs(rte->relid, pf->whereClause, list_make1(pe));
                                534                 :                : 
                                535                 :           3708 :             qual_exprs = lappend(qual_exprs, tr);
                                536                 :                :         }
                                537                 :                :     }
                                538                 :                : 
                                539         [ +  + ]:            839 :     if (rte->graph_pattern->whereClause)
                                540                 :                :     {
                                541                 :            338 :         Node       *path_quals = replace_property_refs(rte->relid,
                                542                 :            338 :                                                        (Node *) rte->graph_pattern->whereClause,
                                543                 :                :                                                        graph_path);
                                544                 :                : 
                                545                 :            338 :         qual_exprs = lappend(qual_exprs, path_quals);
                                546                 :                :     }
                                547                 :                : 
                                548         [ +  + ]:           1574 :     path_query->jointree = makeFromExpr(fromlist,
                                549                 :            735 :                                         qual_exprs ? (Node *) makeBoolExpr(AND_EXPR, qual_exprs, -1) : NULL);
                                550                 :                : 
                                551                 :                :     /* Construct query targetlist from COLUMNS specification of GRAPH_TABLE. */
                                552                 :            839 :     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                 :            819 :     vars = pull_vars_of_level((Node *) list_make2(qual_exprs, path_query->targetList), 0);
                                563   [ +  +  +  +  :           9645 :     foreach_node(Var, var, vars)
                                              +  + ]
                                564                 :                :     {
                                565                 :           8007 :         RTEPermissionInfo *perminfo = getRTEPermissionInfo(path_query->rteperminfos,
                                566                 :           8007 :                                                            rt_fetch(var->varno, path_query->rtable));
                                567                 :                : 
                                568                 :                :         /* Must offset the attnum to fit in a bitmapset */
                                569                 :           8007 :         perminfo->selectedCols = bms_add_member(perminfo->selectedCols,
                                570                 :           8007 :                                                 var->varattno - FirstLowInvalidHeapAttributeNumber);
                                571                 :                :     }
                                572                 :                : 
                                573                 :            819 :     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                 :            445 : generate_union_from_pathqueries(List **pathqueries)
                                621                 :                : {
                                622                 :            445 :     List       *rtable = NIL;
                                623                 :            445 :     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         [ -  + ]:            445 :     Assert(list_length(*pathqueries) > 0);
                                633                 :                : 
                                634                 :                :     /* If there's only one pathquery, no need to construct a UNION query. */
                                635         [ +  + ]:            445 :     if (list_length(*pathqueries) == 1)
                                636                 :                :     {
                                637                 :            317 :         *pathqueries = NIL;
                                638                 :            317 :         return sampleQuery;
                                639                 :                :     }
                                640                 :                : 
                                641                 :            128 :     sostmt = castNode(SetOperationStmt,
                                642                 :                :                       generate_setop_from_pathqueries(*pathqueries, &rtable, NULL));
                                643                 :                : 
                                644                 :                :     /* Encapsulate the set operation statement into a Query. */
                                645                 :            128 :     union_query = makeNode(Query);
                                646                 :            128 :     union_query->commandType = CMD_SELECT;
                                647                 :            128 :     union_query->rtable = rtable;
                                648                 :            128 :     union_query->setOperations = (Node *) sostmt;
                                649                 :            128 :     union_query->rteperminfos = NIL;
                                650                 :            128 :     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         [ -  + ]:            128 :     Assert(rt_fetch(1, rtable));
                                662                 :            128 :     union_query->targetList = NULL;
                                663                 :            128 :     resno = 1;
                                664   [ +  -  +  +  :            463 :     forfour(lct, sostmt->colTypes,
                                     +  -  +  +  +  
                                     -  +  +  +  -  
                                     +  +  +  +  +  
                                     -  +  -  +  -  
                                              +  + ]
                                665                 :                :             lcm, sostmt->colTypmods,
                                666                 :                :             lcc, sostmt->colCollations,
                                667                 :                :             lctl, sampleQuery->targetList)
                                668                 :                :     {
                                669                 :            335 :         Oid         colType = lfirst_oid(lct);
                                670                 :            335 :         int32       colTypmod = lfirst_int(lcm);
                                671                 :            335 :         Oid         colCollation = lfirst_oid(lcc);
                                672                 :            335 :         TargetEntry *sample_tle = (TargetEntry *) lfirst(lctl);
                                673                 :                :         char       *colName;
                                674                 :                :         TargetEntry *tle;
                                675                 :                :         Var        *var;
                                676                 :                : 
                                677         [ -  + ]:            335 :         Assert(!sample_tle->resjunk);
                                678                 :            335 :         colName = pstrdup(sample_tle->resname);
                                679                 :            335 :         var = makeVar(1, sample_tle->resno, colType, colTypmod, colCollation, 0);
                                680                 :            335 :         var->location = exprLocation((Node *) sample_tle->expr);
                                681                 :            335 :         tle = makeTargetEntry((Expr *) var, (AttrNumber) resno++, colName, false);
                                682                 :            335 :         union_query->targetList = lappend(union_query->targetList, tle);
                                683                 :                :     }
                                684                 :                : 
                                685                 :            128 :     *pathqueries = NIL;
                                686                 :            128 :     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                 :            634 : generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist)
                                697                 :                : {
                                698                 :                :     SetOperationStmt *sostmt;
                                699                 :                :     Query      *lquery;
                                700                 :                :     Node       *rarg;
                                701                 :            634 :     RangeTblRef *lrtr = makeNode(RangeTblRef);
                                702                 :                :     List       *rtargetlist;
                                703                 :                :     ParseNamespaceItem *pni;
                                704                 :                : 
                                705                 :                :     /* Guard against stack overflow due to many path queries. */
   11                           706                 :            634 :     check_stack_depth();
                                707                 :                : 
                                708                 :                :     /* Recursion termination condition. */
   50                           709         [ +  + ]:            634 :     if (list_length(pathqueries) == 0)
                                710                 :                :     {
                                711                 :            128 :         *targetlist = NIL;
                                712                 :            128 :         return NULL;
                                713                 :                :     }
                                714                 :                : 
                                715                 :            506 :     lquery = linitial_node(Query, pathqueries);
                                716                 :                : 
                                717                 :            506 :     pni = addRangeTableEntryForSubquery(make_parsestate(NULL), lquery, NULL,
                                718                 :                :                                         false, false);
                                719                 :            506 :     *rtable = lappend(*rtable, pni->p_rte);
                                720                 :            506 :     lrtr->rtindex = list_length(*rtable);
                                721                 :            506 :     rarg = generate_setop_from_pathqueries(list_delete_first(pathqueries), rtable, &rtargetlist);
                                722         [ +  + ]:            506 :     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         [ +  - ]:            128 :         if (targetlist)
                                730                 :                :         {
                                731                 :            128 :             *targetlist = NIL;
                                732   [ +  -  +  +  :            591 :             foreach_node(TargetEntry, tle, lquery->targetList)
                                              +  + ]
                                733                 :                :             {
                                734         [ +  - ]:            335 :                 if (!tle->resjunk)
                                735                 :            335 :                     *targetlist = lappend(*targetlist, tle);
                                736                 :                :             }
                                737                 :                :         }
                                738                 :            128 :         return (Node *) lrtr;
                                739                 :                :     }
                                740                 :                : 
                                741                 :            378 :     sostmt = makeNode(SetOperationStmt);
                                742                 :            378 :     sostmt->op = SETOP_UNION;
                                743                 :            378 :     sostmt->all = true;
                                744                 :            378 :     sostmt->larg = (Node *) lrtr;
                                745                 :            378 :     sostmt->rarg = rarg;
                                746                 :            378 :     constructSetOpTargetlist(NULL, sostmt, lquery->targetList, rtargetlist, targetlist, "UNION", false);
                                747                 :                : 
                                748                 :            378 :     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                 :           4594 : create_pe_for_element(struct path_factor *pf, Oid elemoid)
                                760                 :                : {
                                761                 :           4594 :     HeapTuple   eletup = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(elemoid));
                                762                 :                :     Form_pg_propgraph_element pgeform;
                                763                 :                :     struct path_element *pe;
                                764                 :                : 
                                765         [ -  + ]:           4594 :     if (!eletup)
   50 peter@eisentraut.org      766         [ #  # ]:UNC           0 :         elog(ERROR, "cache lookup failed for property graph element %u", elemoid);
   50 peter@eisentraut.org      767                 :GNC        4594 :     pgeform = ((Form_pg_propgraph_element) GETSTRUCT(eletup));
                                768                 :                : 
                                769   [ +  +  +  + ]:           4594 :     if ((pgeform->pgekind == PGEKIND_VERTEX && pf->kind != VERTEX_PATTERN) ||
                                770   [ +  +  +  +  :           3972 :         (pgeform->pgekind == PGEKIND_EDGE && !IS_EDGE_PATTERN(pf->kind)))
                                        +  +  +  + ]
                                771                 :                :     {
                                772                 :           1936 :         ReleaseSysCache(eletup);
                                773                 :           1936 :         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   [ +  -  -  + ]:           1228 :         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                 :           1297 : get_labels_for_expr(Oid propgraphid, Node *labelexpr)
                                823                 :                : {
                                824                 :                :     List       *label_oids;
                                825                 :                : 
                                826         [ +  + ]:           1297 :     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                 :            410 :         label_oids = NIL;
                                840                 :            410 :         rel = table_open(PropgraphLabelRelationId, AccessShareLock);
                                841                 :            410 :         ScanKeyInit(&key[0],
                                842                 :                :                     Anum_pg_propgraph_label_pglpgid,
                                843                 :                :                     BTEqualStrategyNumber,
                                844                 :                :                     F_OIDEQ, ObjectIdGetDatum(propgraphid));
                                845                 :            410 :         scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId,
                                846                 :                :                                   true, NULL, 1, key);
                                847         [ +  + ]:           2742 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
                                848                 :                :         {
                                849                 :           2332 :             Form_pg_propgraph_label label = (Form_pg_propgraph_label) GETSTRUCT(tup);
                                850                 :                : 
                                851                 :           2332 :             label_oids = lappend_oid(label_oids, label->oid);
                                852                 :                :         }
                                853                 :            410 :         systable_endscan(scan);
                                854                 :            410 :         table_close(rel, AccessShareLock);
                                855                 :                :     }
                                856         [ +  + ]:            887 :     else if (IsA(labelexpr, GraphLabelRef))
                                857                 :                :     {
                                858                 :            846 :         GraphLabelRef *glr = castNode(GraphLabelRef, labelexpr);
                                859                 :                : 
                                860                 :            846 :         label_oids = list_make1_oid(glr->labelid);
                                861                 :                :     }
                                862         [ +  - ]:             41 :     else if (IsA(labelexpr, BoolExpr))
                                863                 :                :     {
                                864                 :             41 :         BoolExpr   *be = castNode(BoolExpr, labelexpr);
                                865                 :             41 :         List       *label_exprs = be->args;
                                866                 :                : 
                                867                 :             41 :         label_oids = NIL;
                                868   [ +  -  +  +  :            168 :         foreach_node(GraphLabelRef, glr, label_exprs)
                                              +  + ]
                                869                 :             86 :             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                 :                :          */
   50 peter@eisentraut.org      877         [ #  # ]:UNC           0 :         elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
                                878                 :                :     }
                                879                 :                : 
   50 peter@eisentraut.org      880                 :GNC        1297 :     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                 :           1297 : get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf)
                                901                 :                : {
                                902                 :           1297 :     List       *label_oids = get_labels_for_expr(propgraphid, pf->labelexpr);
                                903                 :           1297 :     List       *elem_oids_seen = NIL;
                                904                 :           1297 :     List       *pf_elem_oids = NIL;
                                905                 :           1297 :     List       *path_elements = NIL;
                                906                 :           1297 :     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   [ +  +  +  +  :           1297 :     Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
                                        +  +  -  + ]
                                918                 :                : 
                                919                 :           1297 :     rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
                                920   [ +  -  +  +  :           5838 :     foreach_oid(labeloid, label_oids)
                                              +  + ]
                                921                 :                :     {
                                922                 :           3260 :         bool        found = false;
                                923                 :                : 
                                924                 :           3260 :         ScanKeyInit(&key[0],
                                925                 :                :                     Anum_pg_propgraph_element_label_pgellabelid,
                                926                 :                :                     BTEqualStrategyNumber,
                                927                 :                :                     F_OIDEQ, ObjectIdGetDatum(labeloid));
                                928                 :           3260 :         scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true,
                                929                 :                :                                   NULL, 1, key);
                                930         [ +  + ]:          10943 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
                                931                 :                :         {
                                932                 :           7683 :             Form_pg_propgraph_element_label label_elem = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
                                933                 :           7683 :             Oid         elem_oid = label_elem->pgelelid;
                                934                 :                : 
                                935         [ +  + ]:           7683 :             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                 :           4594 :                 struct path_element *pe = create_pe_for_element(pf, elem_oid);
                                942                 :                : 
                                943         [ +  + ]:           4594 :                 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                 :           4594 :                 elem_oids_seen = lappend_oid(elem_oids_seen, label_elem->pgelelid);
                                957                 :                :             }
                                958         [ +  + ]:           3089 :             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                 :           1585 :                 found = true;
                                966                 :                :             }
                                967                 :                :         }
                                968                 :                : 
                                969         [ +  + ]:           3260 :         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         [ +  + ]:            990 :             if (!pf->labelexpr)
                                979                 :            982 :                 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                 :           3252 :         systable_endscan(scan);
                                990                 :                :     }
                                991                 :           1289 :     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                 :           1289 :     pf->labeloids = list_difference_oid(label_oids, unresolved_labels);
                               1000                 :                : 
                               1001                 :           1289 :     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                 :          45144 : replace_property_refs_mutator(Node *node, struct replace_property_refs_context *context)
                               1016                 :                : {
                               1017         [ -  + ]:          45144 :     if (node == NULL)
   50 peter@eisentraut.org     1018                 :UNC           0 :         return NULL;
   50 peter@eisentraut.org     1019         [ +  + ]:GNC       45144 :     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         [ +  + ]:          45136 :     else if (IsA(node, GraphPropertyRef))
                               1034                 :                :     {
                               1035                 :          10861 :         GraphPropertyRef *gpr = (GraphPropertyRef *) node;
                               1036                 :          10861 :         Node       *n = NULL;
                               1037                 :          10861 :         struct path_element *found_mapping = NULL;
                               1038                 :          10861 :         struct path_factor *mapping_factor = NULL;
                               1039                 :          10861 :         List       *unrelated_labels = NIL;
                               1040                 :                : 
                               1041   [ +  -  +  -  :          24926 :         foreach_ptr(struct path_element, m, context->mappings)
                                              +  + ]
                               1042                 :                :         {
                               1043   [ +  +  +  + ]:          14065 :             if (m->path_factor->variable && strcmp(gpr->elvarname, m->path_factor->variable) == 0)
                               1044                 :                :             {
                               1045                 :          10861 :                 found_mapping = m;
                               1046                 :          10861 :                 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         [ -  + ]:          10861 :         Assert(found_mapping);
                               1056                 :                : 
                               1057                 :          10861 :         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   [ +  -  +  +  :          59621 :         foreach_oid(labeloid, mapping_factor->labeloids)
                                              +  + ]
                               1064                 :                :         {
                               1065                 :          37899 :             Oid         elem_labelid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
                               1066                 :                :                                                        Anum_pg_propgraph_element_label_oid,
                               1067                 :                :                                                        ObjectIdGetDatum(found_mapping->elemoid),
                               1068                 :                :                                                        ObjectIdGetDatum(labeloid));
                               1069                 :                : 
                               1070         [ +  + ]:          37899 :             if (OidIsValid(elem_labelid))
                               1071                 :                :             {
                               1072                 :          22919 :                 HeapTuple   tup = SearchSysCache2(PROPGRAPHLABELPROP, ObjectIdGetDatum(elem_labelid),
                               1073                 :                :                                                   ObjectIdGetDatum(gpr->propid));
                               1074                 :                : 
                               1075         [ +  + ]:          22919 :                 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                 :          11199 :                 n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHLABELPROP,
                               1086                 :                :                                                                             tup, Anum_pg_propgraph_label_property_plpexpr)));
                               1087                 :          11199 :                 ChangeVarNodes(n, 1, mapping_factor->factorpos + 1, 0);
                               1088                 :                : 
                               1089                 :          11199 :                 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         [ +  + ]:          10861 :         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         [ +  + ]:          10861 :         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                 :          10841 :         return n;
                               1142                 :                :     }
                               1143                 :                : 
                               1144                 :          34275 :     return expression_tree_mutator(node, replace_property_refs_mutator, context);
                               1145                 :                : }
                               1146                 :                : 
                               1147                 :                : static Node *
                               1148                 :           4885 : replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
                               1149                 :                : {
                               1150                 :                :     struct replace_property_refs_context context;
                               1151                 :                : 
                               1152                 :           4885 :     context.mappings = mappings;
                               1153                 :           4885 :     context.propgraphid = propgraphid;
                               1154                 :                : 
                               1155                 :           4885 :     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)
   50 peter@eisentraut.org     1188         [ #  # ]:UNC           0 :         elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
   50 peter@eisentraut.org     1189         [ -  + ]:GNC        2456 :     if (n1 != n3)
   50 peter@eisentraut.org     1190         [ #  # ]:UNC           0 :         elog(ERROR, "array size key (%d) vs operator (%d) mismatch for element ID %u", catalog_key_attnum, catalog_eqop_attnum, pgeform->oid);
                               1191                 :                : 
   50 peter@eisentraut.org     1192         [ +  + ]:GNC        5582 :     for (int i = 0; i < n1; i++)
                               1193                 :                :     {
                               1194                 :           3126 :         AttrNumber  keyattn = DatumGetInt16(d1[i]);
                               1195                 :           3126 :         AttrNumber  refattn = DatumGetInt16(d2[i]);
                               1196                 :           3126 :         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                 :           3126 :         get_atttypetypmodcoll(pgeform->pgerelid, keyattn, &atttypid, &atttypmod, &attcoll);
                               1210                 :           3126 :         keyvar = makeVar(edgerti, keyattn, atttypid, atttypmod, attcoll, 0);
                               1211                 :           3126 :         get_atttypetypmodcoll(refrelid, refattn, &atttypid, &atttypmod, &attcoll);
                               1212                 :           3126 :         refvar = makeVar(refrti, refattn, atttypid, atttypmod, attcoll, 0);
                               1213                 :                : 
                               1214                 :           3126 :         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(eqop));
                               1215         [ -  + ]:           3126 :         if (!HeapTupleIsValid(tup))
   50 peter@eisentraut.org     1216         [ #  # ]:UNC           0 :             elog(ERROR, "cache lookup failed for operator %u", eqop);
   50 peter@eisentraut.org     1217                 :GNC        3126 :         opform = (Form_pg_operator) GETSTRUCT(tup);
                               1218                 :                :         /* An equality operator is a binary operator returning boolean result. */
                               1219   [ +  -  +  -  :           3126 :         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                 :           3126 :         args = list_make2(refvar, keyvar);
                               1231                 :           3126 :         actual_arg_types[0] = exprType((Node *) refvar);
                               1232                 :           3126 :         actual_arg_types[1] = exprType((Node *) keyvar);
                               1233                 :           3126 :         declared_arg_types[0] = opform->oprleft;
                               1234                 :           3126 :         declared_arg_types[1] = opform->oprright;
                               1235                 :           3126 :         make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
                               1236                 :                : 
                               1237                 :           3126 :         linkqual = makeNode(OpExpr);
                               1238                 :           3126 :         linkqual->opno = opform->oid;
                               1239                 :           3126 :         linkqual->opfuncid = opform->oprcode;
                               1240                 :           3126 :         linkqual->opresulttype = opform->oprresult;
                               1241                 :           3126 :         linkqual->opretset = false;
                               1242                 :                :         /* opcollid and inputcollid will be set by parse_collate.c */
                               1243                 :           3126 :         linkqual->args = args;
                               1244                 :           3126 :         linkqual->location = -1;
                               1245                 :                : 
                               1246                 :           3126 :         ReleaseSysCache(tup);
                               1247                 :           3126 :         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.5.0-beta