LCOV - code coverage report
Current view: top level - src/backend/parser - parse_graphtable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.8 % 120 109
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * parse_graphtable.c
       4              :  *    parsing of GRAPH_TABLE
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/parser/parse_graphtable.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : 
      16              : #include "postgres.h"
      17              : 
      18              : #include "access/genam.h"
      19              : #include "access/htup_details.h"
      20              : #include "access/table.h"
      21              : #include "catalog/pg_propgraph_label.h"
      22              : #include "catalog/pg_propgraph_property.h"
      23              : #include "miscadmin.h"
      24              : #include "nodes/makefuncs.h"
      25              : #include "parser/parse_collate.h"
      26              : #include "parser/parse_expr.h"
      27              : #include "parser/parse_graphtable.h"
      28              : #include "parser/parse_node.h"
      29              : #include "utils/fmgroids.h"
      30              : #include "utils/lsyscache.h"
      31              : #include "utils/relcache.h"
      32              : #include "utils/syscache.h"
      33              : 
      34              : 
      35              : /*
      36              :  * Return human-readable name of the type of graph element pattern in
      37              :  * GRAPH_TABLE clause, usually for error message purpose.
      38              :  */
      39              : static const char *
      40            4 : get_gep_kind_name(GraphElementPatternKind gepkind)
      41              : {
      42            4 :     switch (gepkind)
      43              :     {
      44            0 :         case VERTEX_PATTERN:
      45            0 :             return "vertex";
      46            0 :         case EDGE_PATTERN_LEFT:
      47            0 :             return "edge pointing left";
      48            0 :         case EDGE_PATTERN_RIGHT:
      49            0 :             return "edge pointing right";
      50            0 :         case EDGE_PATTERN_ANY:
      51            0 :             return "edge pointing any direction";
      52            4 :         case PAREN_EXPR:
      53            4 :             return "nested path pattern";
      54              :     }
      55              : 
      56              :     /*
      57              :      * When a GraphElementPattern is constructed by the parser, it will set a
      58              :      * value from the GraphElementPatternKind enum. But we may get here if the
      59              :      * GraphElementPatternKind value stored in a catalog is corrupted.
      60              :      */
      61            0 :     return "unknown";
      62              : }
      63              : 
      64              : /*
      65              :  * Transform a property reference.
      66              :  *
      67              :  * A property reference is parsed as a ColumnRef of the form:
      68              :  * <variable>.<property>. If <variable> is one of the variables bound to an
      69              :  * element pattern in the graph pattern and <property> can be resolved as a
      70              :  * property of the property graph, then we return a GraphPropertyRef node
      71              :  * representing the property reference. If the <variable> exists in the graph
      72              :  * pattern but <property> does not exist in the property graph, we raise an
      73              :  * error. However, if <variable> does not exist in the graph pattern, we return
      74              :  * NULL to let the caller handle it as some other kind of ColumnRef. The
      75              :  * variables bound to the element patterns in the graph pattern are expected to
      76              :  * be collected in the GraphTableParseState.
      77              :  */
      78              : Node *
      79      1181530 : transformGraphTablePropertyRef(ParseState *pstate, ColumnRef *cref)
      80              : {
      81      1181530 :     GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
      82              : 
      83      1181530 :     if (!gpstate)
      84      1179842 :         return NULL;
      85              : 
      86         1688 :     if (list_length(cref->fields) == 2)
      87              :     {
      88         1675 :         Node       *field1 = linitial(cref->fields);
      89         1675 :         Node       *field2 = lsecond(cref->fields);
      90              :         char       *elvarname;
      91              :         char       *propname;
      92              : 
      93         1675 :         if (IsA(field1, A_Star) || IsA(field2, A_Star))
      94              :         {
      95            8 :             if (pstate->p_expr_kind == EXPR_KIND_SELECT_TARGET)
      96            4 :                 ereport(ERROR,
      97              :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      98              :                         errmsg("\"*\" is not supported here"),
      99              :                         parser_errposition(pstate, cref->location));
     100              :             else
     101            4 :                 ereport(ERROR,
     102              :                         errcode(ERRCODE_SYNTAX_ERROR),
     103              :                         errmsg("\"*\" not allowed here"),
     104              :                         parser_errposition(pstate, cref->location));
     105              :         }
     106              : 
     107         1667 :         elvarname = strVal(field1);
     108         1667 :         propname = strVal(field2);
     109              : 
     110         1667 :         if (list_member(gpstate->variables, field1))
     111              :         {
     112              :             GraphPropertyRef *gpr;
     113              :             HeapTuple   pgptup;
     114              :             Form_pg_propgraph_property pgpform;
     115              : 
     116              :             /*
     117              :              * If we are transforming expression in an element pattern,
     118              :              * property references containing only that variable are allowed.
     119              :              */
     120         1655 :             if (gpstate->cur_gep)
     121              :             {
     122          159 :                 if (!gpstate->cur_gep->variable ||
     123          151 :                     strcmp(elvarname, gpstate->cur_gep->variable) != 0)
     124           16 :                     ereport(ERROR,
     125              :                             errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     126              :                             errmsg("non-local element variable reference is not supported"),
     127              :                             parser_errposition(pstate, cref->location));
     128              :             }
     129              : 
     130         1639 :             gpr = makeNode(GraphPropertyRef);
     131         1639 :             pgptup = SearchSysCache2(PROPGRAPHPROPNAME, ObjectIdGetDatum(gpstate->graphid), CStringGetDatum(propname));
     132         1639 :             if (!HeapTupleIsValid(pgptup))
     133            4 :                 ereport(ERROR,
     134              :                         errcode(ERRCODE_SYNTAX_ERROR),
     135              :                         errmsg("property \"%s\" does not exist", propname));
     136         1635 :             pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup);
     137              : 
     138         1635 :             gpr->location = cref->location;
     139         1635 :             gpr->elvarname = elvarname;
     140         1635 :             gpr->propid = pgpform->oid;
     141         1635 :             gpr->typeId = pgpform->pgptypid;
     142         1635 :             gpr->typmod = pgpform->pgptypmod;
     143         1635 :             gpr->collation = pgpform->pgpcollation;
     144              : 
     145         1635 :             ReleaseSysCache(pgptup);
     146              : 
     147         1635 :             return (Node *) gpr;
     148              :         }
     149              :     }
     150              : 
     151           25 :     return NULL;
     152              : }
     153              : 
     154              : /*
     155              :  * Transform a label expression.
     156              :  *
     157              :  * A label expression is parsed as either a ColumnRef with a single field or a
     158              :  * label expression like label disjunction. The single field in the ColumnRef is
     159              :  * treated as a label name and transformed to a GraphLabelRef node. The label
     160              :  * expression is recursively transformed into an expression tree containg
     161              :  * GraphLabelRef nodes corresponding to the names of the labels appearing in the
     162              :  * expression. If any label name cannot be resolved to a label in the property
     163              :  * graph, an error is raised.
     164              :  */
     165              : static Node *
     166         1773 : transformLabelExpr(GraphTableParseState *gpstate, Node *labelexpr)
     167              : {
     168              :     Node       *result;
     169              : 
     170         1773 :     if (labelexpr == NULL)
     171          665 :         return NULL;
     172              : 
     173         1108 :     check_stack_depth();
     174              : 
     175         1108 :     switch (nodeTag(labelexpr))
     176              :     {
     177         1056 :         case T_ColumnRef:
     178              :             {
     179         1056 :                 ColumnRef  *cref = (ColumnRef *) labelexpr;
     180              :                 const char *labelname;
     181              :                 Oid         labelid;
     182              :                 GraphLabelRef *lref;
     183              : 
     184              :                 Assert(list_length(cref->fields) == 1);
     185         1056 :                 labelname = strVal(linitial(cref->fields));
     186              : 
     187         1056 :                 labelid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(gpstate->graphid), CStringGetDatum(labelname));
     188         1056 :                 if (!labelid)
     189            4 :                     ereport(ERROR,
     190              :                             errcode(ERRCODE_UNDEFINED_OBJECT),
     191              :                             errmsg("label \"%s\" does not exist in property graph \"%s\"", labelname, get_rel_name(gpstate->graphid)));
     192              : 
     193         1052 :                 lref = makeNode(GraphLabelRef);
     194         1052 :                 lref->labelid = labelid;
     195         1052 :                 lref->location = cref->location;
     196              : 
     197         1052 :                 result = (Node *) lref;
     198         1052 :                 break;
     199              :             }
     200              : 
     201           52 :         case T_BoolExpr:
     202              :             {
     203           52 :                 BoolExpr   *be = (BoolExpr *) labelexpr;
     204              :                 ListCell   *lc;
     205           52 :                 List       *args = NIL;
     206              : 
     207          156 :                 foreach(lc, be->args)
     208              :                 {
     209          108 :                     Node       *arg = (Node *) lfirst(lc);
     210              : 
     211          108 :                     arg = transformLabelExpr(gpstate, arg);
     212          104 :                     args = lappend(args, arg);
     213              :                 }
     214              : 
     215           48 :                 result = (Node *) makeBoolExpr(be->boolop, args, be->location);
     216           48 :                 break;
     217              :             }
     218              : 
     219            0 :         default:
     220              :             /* should not reach here */
     221            0 :             elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
     222              :             result = NULL;      /* keep compiler quiet */
     223              :             break;
     224              :     }
     225              : 
     226         1100 :     return result;
     227              : }
     228              : 
     229              : /*
     230              :  * Transform a GraphElementPattern.
     231              :  *
     232              :  * Transform the label expression and the where clause in the element pattern
     233              :  * given by GraphElementPattern. The variable name in the GraphElementPattern is
     234              :  * added to the list of variables in the GraphTableParseState which is used to
     235              :  * resolve property references in this element pattern or elsewhere in the
     236              :  * GRAPH_TABLE.
     237              :  */
     238              : static Node *
     239         1669 : transformGraphElementPattern(ParseState *pstate, GraphElementPattern *gep)
     240              : {
     241         1669 :     GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
     242              : 
     243         1669 :     if (gep->quantifier)
     244            4 :         ereport(ERROR,
     245              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     246              :                  errmsg("element pattern quantifier is not supported")));
     247              : 
     248              :     Assert(!gpstate->cur_gep);
     249              : 
     250         1665 :     gpstate->cur_gep = gep;
     251              : 
     252         1665 :     gep->labelexpr = transformLabelExpr(gpstate, gep->labelexpr);
     253              : 
     254         1661 :     gep->whereClause = transformExpr(pstate, gep->whereClause, EXPR_KIND_WHERE);
     255         1641 :     assign_expr_collations(pstate, gep->whereClause);
     256              : 
     257         1641 :     gpstate->cur_gep = NULL;
     258              : 
     259         1641 :     return (Node *) gep;
     260              : }
     261              : 
     262              : /*
     263              :  * Transform a path term (list of GraphElementPattern's).
     264              :  */
     265              : static Node *
     266          561 : transformPathTerm(ParseState *pstate, List *path_term)
     267              : {
     268          561 :     List       *result = NIL;
     269          561 :     GraphElementPattern *prev_gep = NULL;
     270              : 
     271         2719 :     foreach_node(GraphElementPattern, gep, path_term)
     272              :     {
     273         1685 :         if (gep->kind != VERTEX_PATTERN && !IS_EDGE_PATTERN(gep->kind))
     274            4 :             ereport(ERROR,
     275              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     276              :                      errmsg("unsupported element pattern kind: \"%s\"", get_gep_kind_name(gep->kind)),
     277              :                      parser_errposition(pstate, gep->location)));
     278              : 
     279         1681 :         if (IS_EDGE_PATTERN(gep->kind))
     280              :         {
     281          574 :             if (!prev_gep)
     282            4 :                 ereport(ERROR,
     283              :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     284              :                          errmsg("path pattern cannot start with an edge pattern"),
     285              :                          parser_errposition(pstate, gep->location)));
     286          570 :             else if (prev_gep->kind != VERTEX_PATTERN)
     287            4 :                 ereport(ERROR,
     288              :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     289              :                          errmsg("edge pattern must be preceded by a vertex pattern"),
     290              :                          parser_errposition(pstate, gep->location)));
     291              :         }
     292              :         else
     293              :         {
     294         1107 :             if (prev_gep && !IS_EDGE_PATTERN(prev_gep->kind))
     295            4 :                 ereport(ERROR,
     296              :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     297              :                          errmsg("adjacent vertex patterns are not supported"),
     298              :                          parser_errposition(pstate, gep->location)));
     299              :         }
     300              : 
     301         1641 :         result = lappend(result,
     302         1669 :                          transformGraphElementPattern(pstate, gep));
     303         1641 :         prev_gep = gep;
     304              :     }
     305              : 
     306              :     /* Path pattern should have at least one element pattern. */
     307              :     Assert(prev_gep);
     308              : 
     309          517 :     if (IS_EDGE_PATTERN(prev_gep->kind))
     310              :     {
     311            4 :         ereport(ERROR,
     312              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     313              :                  errmsg("path pattern cannot end with an edge pattern"),
     314              :                  parser_errposition(pstate, prev_gep->location)));
     315              :     }
     316              : 
     317          513 :     return (Node *) result;
     318              : }
     319              : 
     320              : /*
     321              :  * Transform a path pattern list (list of path terms).
     322              :  */
     323              : static Node *
     324          565 : transformPathPatternList(ParseState *pstate, List *path_pattern)
     325              : {
     326          565 :     List       *result = NIL;
     327          565 :     GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
     328              : 
     329              :     Assert(gpstate);
     330              : 
     331              :     /* Grammar doesn't allow empty path pattern list */
     332              :     Assert(list_length(path_pattern) > 0);
     333              : 
     334              :     /*
     335              :      * We do not support multiple path patterns in one GRAPH_TABLE clause
     336              :      * right now. But we may do so in future.
     337              :      */
     338          565 :     if (list_length(path_pattern) != 1)
     339            4 :         ereport(ERROR,
     340              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     341              :                  errmsg("multiple path patterns in one GRAPH_TABLE clause not supported")));
     342              : 
     343              :     /*
     344              :      * Collect all the variables in the path pattern into the
     345              :      * GraphTableParseState so that we can detect any non-local element
     346              :      * variable references. We need to do this before transforming the path
     347              :      * pattern so as to detect forward references to element variables in the
     348              :      * WHERE clause of an element pattern.
     349              :      */
     350         1683 :     foreach_node(List, path_term, path_pattern)
     351              :     {
     352         2851 :         foreach_node(GraphElementPattern, gep, path_term)
     353              :         {
     354         1729 :             if (gep->variable)
     355         1370 :                 gpstate->variables = list_append_unique(gpstate->variables, makeString(pstrdup(gep->variable)));
     356              :         }
     357              :     }
     358              : 
     359         1587 :     foreach_node(List, path_term, path_pattern)
     360          561 :         result = lappend(result, transformPathTerm(pstate, path_term));
     361              : 
     362          513 :     return (Node *) result;
     363              : }
     364              : 
     365              : /*
     366              :  * Transform a GraphPattern.
     367              :  *
     368              :  * A GraphPattern consists of a list of one or more path patterns and an
     369              :  * optional where clause. Transform them. We use the previously constructure
     370              :  * list of variables in the GraphTableParseState to resolve property references
     371              :  * in the WHERE clause.
     372              :  */
     373              : Node *
     374          565 : transformGraphPattern(ParseState *pstate, GraphPattern *graph_pattern)
     375              : {
     376          565 :     List       *path_pattern_list = castNode(List,
     377              :                                              transformPathPatternList(pstate, graph_pattern->path_pattern_list));
     378              : 
     379          513 :     graph_pattern->path_pattern_list = path_pattern_list;
     380          513 :     graph_pattern->whereClause = transformExpr(pstate, graph_pattern->whereClause, EXPR_KIND_WHERE);
     381          513 :     assign_expr_collations(pstate, graph_pattern->whereClause);
     382              : 
     383          513 :     return (Node *) graph_pattern;
     384              : }
        

Generated by: LCOV version 2.0-1