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