Age Owner Branch data TLA 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 *
50 peter@eisentraut.org 40 :GNC 4 : get_gep_kind_name(GraphElementPatternKind gepkind)
41 : : {
42 [ - - - - : 4 : switch (gepkind)
+ - ]
43 : : {
50 peter@eisentraut.org 44 :UNC 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";
50 peter@eisentraut.org 52 :GNC 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 : : */
50 peter@eisentraut.org 61 :UNC 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 *
50 peter@eisentraut.org 79 :GNC 1196885 : transformGraphTablePropertyRef(ParseState *pstate, ColumnRef *cref)
80 : : {
81 : 1196885 : GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
82 : :
83 [ + + ]: 1196885 : if (!gpstate)
84 : 1195194 : return NULL;
85 : :
86 [ + + ]: 1691 : if (list_length(cref->fields) == 2)
87 : : {
88 : 1679 : Node *field1 = linitial(cref->fields);
89 : 1679 : Node *field2 = lsecond(cref->fields);
90 : : char *elvarname;
91 : : char *propname;
92 : :
93 [ + - + + ]: 1679 : 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 : 1671 : elvarname = strVal(field1);
108 : 1671 : propname = strVal(field2);
109 : :
110 [ + + ]: 1671 : 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 : : */
35 120 [ + + ]: 1649 : 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 : 1633 : gpr = makeNode(GraphPropertyRef);
50 131 : 1633 : pgptup = SearchSysCache2(PROPGRAPHPROPNAME, ObjectIdGetDatum(gpstate->graphid), CStringGetDatum(propname));
132 [ + + ]: 1633 : if (!HeapTupleIsValid(pgptup))
133 [ + - ]: 4 : ereport(ERROR,
134 : : errcode(ERRCODE_SYNTAX_ERROR),
135 : : errmsg("property \"%s\" does not exist", propname));
136 : 1629 : pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup);
137 : :
138 : 1629 : gpr->location = cref->location;
139 : 1629 : gpr->elvarname = elvarname;
140 : 1629 : gpr->propid = pgpform->oid;
141 : 1629 : gpr->typeId = pgpform->pgptypid;
142 : 1629 : gpr->typmod = pgpform->pgptypmod;
143 : 1629 : gpr->collation = pgpform->pgpcollation;
144 : :
145 : 1629 : ReleaseSysCache(pgptup);
146 : :
147 : 1629 : return (Node *) gpr;
148 : : }
149 : : }
150 : :
151 : 34 : 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 containing
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 : 1767 : transformLabelExpr(GraphTableParseState *gpstate, Node *labelexpr)
167 : : {
168 : : Node *result;
169 : :
170 [ + + ]: 1767 : if (labelexpr == NULL)
171 : 662 : return NULL;
172 : :
173 : 1105 : check_stack_depth();
174 : :
175 [ + + - ]: 1105 : switch (nodeTag(labelexpr))
176 : : {
177 : 1050 : case T_ColumnRef:
178 : : {
179 : 1050 : ColumnRef *cref = (ColumnRef *) labelexpr;
180 : : const char *labelname;
181 : : Oid labelid;
182 : : GraphLabelRef *lref;
183 : :
184 [ - + ]: 1050 : Assert(list_length(cref->fields) == 1);
185 : 1050 : labelname = strVal(linitial(cref->fields));
186 : :
187 : 1050 : labelid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(gpstate->graphid), CStringGetDatum(labelname));
188 [ + + ]: 1050 : 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 : 1046 : lref = makeNode(GraphLabelRef);
194 : 1046 : lref->labelid = labelid;
195 : 1046 : lref->location = cref->location;
196 : :
197 : 1046 : result = (Node *) lref;
198 : 1046 : break;
199 : : }
200 : :
201 : 55 : case T_BoolExpr:
202 : : {
203 : 55 : BoolExpr *be = (BoolExpr *) labelexpr;
204 : : ListCell *lc;
205 : 55 : List *args = NIL;
206 : :
207 [ + - + + : 165 : foreach(lc, be->args)
+ + ]
208 : : {
209 : 114 : Node *arg = (Node *) lfirst(lc);
210 : :
211 : 114 : arg = transformLabelExpr(gpstate, arg);
212 : 110 : args = lappend(args, arg);
213 : : }
214 : :
215 : 51 : result = (Node *) makeBoolExpr(be->boolop, args, be->location);
216 : 51 : break;
217 : : }
218 : :
50 peter@eisentraut.org 219 :UNC 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 : :
50 peter@eisentraut.org 226 :GNC 1097 : 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 : 1657 : transformGraphElementPattern(ParseState *pstate, GraphElementPattern *gep)
240 : : {
241 : 1657 : GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
242 : :
243 [ + + ]: 1657 : if (gep->quantifier)
244 [ + - ]: 4 : ereport(ERROR,
245 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
246 : : errmsg("element pattern quantifier is not supported")));
247 : :
35 248 [ - + ]: 1653 : Assert(!gpstate->cur_gep);
249 : :
250 : 1653 : gpstate->cur_gep = gep;
251 : :
50 252 : 1653 : gep->labelexpr = transformLabelExpr(gpstate, gep->labelexpr);
253 : :
254 : 1649 : gep->whereClause = transformExpr(pstate, gep->whereClause, EXPR_KIND_WHERE);
255 : :
256 : : /*
257 : : * Assign collations here for the reason mentioned in the prologue of
258 : : * transformGraphPattern().
259 : : */
260 : 1629 : assign_expr_collations(pstate, gep->whereClause);
261 : :
35 262 : 1629 : gpstate->cur_gep = NULL;
263 : :
50 264 : 1629 : return (Node *) gep;
265 : : }
266 : :
267 : : /*
268 : : * Transform a path term (list of GraphElementPattern's).
269 : : */
270 : : static Node *
271 : 559 : transformPathTerm(ParseState *pstate, List *path_term)
272 : : {
273 : 559 : List *result = NIL;
39 274 : 559 : GraphElementPattern *prev_gep = NULL;
275 : :
50 276 [ + - + + : 2703 : foreach_node(GraphElementPattern, gep, path_term)
+ + ]
277 : : {
39 278 [ + + + + : 1673 : if (gep->kind != VERTEX_PATTERN && !IS_EDGE_PATTERN(gep->kind))
+ + + + ]
279 [ + - ]: 4 : ereport(ERROR,
280 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
281 : : errmsg("unsupported element pattern kind: \"%s\"", get_gep_kind_name(gep->kind)),
282 : : parser_errposition(pstate, gep->location)));
283 : :
284 [ + + + + : 1669 : if (IS_EDGE_PATTERN(gep->kind))
+ + ]
285 : : {
286 [ + + ]: 569 : if (!prev_gep)
287 [ + - ]: 4 : ereport(ERROR,
288 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
289 : : errmsg("path pattern cannot start with an edge pattern"),
290 : : parser_errposition(pstate, gep->location)));
291 [ + + ]: 565 : else if (prev_gep->kind != VERTEX_PATTERN)
292 [ + - ]: 4 : ereport(ERROR,
293 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
294 : : errmsg("edge pattern must be preceded by a vertex pattern"),
295 : : parser_errposition(pstate, gep->location)));
296 : : }
297 : : else
298 : : {
299 [ + + + + : 1100 : if (prev_gep && !IS_EDGE_PATTERN(prev_gep->kind))
+ + + + ]
300 [ + - ]: 4 : ereport(ERROR,
301 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
302 : : errmsg("adjacent vertex patterns are not supported"),
303 : : parser_errposition(pstate, gep->location)));
304 : : }
305 : :
50 306 : 1629 : result = lappend(result,
307 : 1657 : transformGraphElementPattern(pstate, gep));
39 308 : 1629 : prev_gep = gep;
309 : : }
310 : :
311 : : /* Path pattern should have at least one element pattern. */
312 [ - + ]: 515 : Assert(prev_gep);
313 : :
314 [ + + + - : 515 : if (IS_EDGE_PATTERN(prev_gep->kind))
- + ]
315 : : {
316 [ + - ]: 4 : ereport(ERROR,
317 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
318 : : errmsg("path pattern cannot end with an edge pattern"),
319 : : parser_errposition(pstate, prev_gep->location)));
320 : : }
321 : :
50 322 : 511 : return (Node *) result;
323 : : }
324 : :
325 : : /*
326 : : * Transform a path pattern list (list of path terms).
327 : : */
328 : : static Node *
329 : 563 : transformPathPatternList(ParseState *pstate, List *path_pattern)
330 : : {
331 : 563 : List *result = NIL;
35 332 : 563 : GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
333 : :
334 [ - + ]: 563 : Assert(gpstate);
335 : :
336 : : /* Grammar doesn't allow empty path pattern list */
50 337 [ - + ]: 563 : Assert(list_length(path_pattern) > 0);
338 : :
339 : : /*
340 : : * We do not support multiple path patterns in one GRAPH_TABLE clause
341 : : * right now. But we may do so in future.
342 : : */
343 [ + + ]: 563 : if (list_length(path_pattern) != 1)
344 [ + - ]: 4 : ereport(ERROR,
345 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
346 : : errmsg("multiple path patterns in one GRAPH_TABLE clause not supported")));
347 : :
348 : : /*
349 : : * Collect all the variables in the path pattern into the
350 : : * GraphTableParseState so that we can detect any non-local element
351 : : * variable references. We need to do this before transforming the path
352 : : * pattern so as to detect forward references to element variables in the
353 : : * WHERE clause of an element pattern.
354 : : */
35 355 [ + - + + : 1677 : foreach_node(List, path_term, path_pattern)
+ + ]
356 : : {
357 [ + - + + : 2835 : foreach_node(GraphElementPattern, gep, path_term)
+ + ]
358 : : {
359 [ + + ]: 1717 : if (gep->variable)
360 : 1364 : gpstate->variables = list_append_unique(gpstate->variables, makeString(pstrdup(gep->variable)));
361 : : }
362 : : }
363 : :
50 364 [ + - + + : 1581 : foreach_node(List, path_term, path_pattern)
+ + ]
365 : 559 : result = lappend(result, transformPathTerm(pstate, path_term));
366 : :
367 : 511 : return (Node *) result;
368 : : }
369 : :
370 : : /*
371 : : * Transform a GraphPattern.
372 : : *
373 : : * A GraphPattern consists of a list of one or more path patterns and an
374 : : * optional where clause. Transform them. We use the previously constructed
375 : : * list of variables in the GraphTableParseState to resolve property references
376 : : * in the WHERE clause.
377 : : *
378 : : * Since most parts of the GraphPattern do not require collation assignment, we
379 : : * assign collations to the required expressions as they are transformed. This
380 : : * avoids the need to traverse the whole GraphPattern again and avoids exposing
381 : : * it to assign_expr_collations().
382 : : */
383 : : Node *
384 : 563 : transformGraphPattern(ParseState *pstate, GraphPattern *graph_pattern)
385 : : {
386 : 563 : List *path_pattern_list = castNode(List,
387 : : transformPathPatternList(pstate, graph_pattern->path_pattern_list));
388 : :
389 : 511 : graph_pattern->path_pattern_list = path_pattern_list;
390 : 511 : graph_pattern->whereClause = transformExpr(pstate, graph_pattern->whereClause, EXPR_KIND_WHERE);
391 : 511 : assign_expr_collations(pstate, graph_pattern->whereClause);
392 : :
393 : 511 : return (Node *) graph_pattern;
394 : : }
|