PostgreSQL Source Code git master
Loading...
Searching...
No Matches
rewriteGraphTable.c
Go to the documentation of this file.
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"
26#include "miscadmin.h"
27#include "nodes/makefuncs.h"
28#include "nodes/nodeFuncs.h"
29#include "optimizer/optimizer.h"
30#include "parser/analyze.h"
32#include "parser/parse_func.h"
33#include "parser/parse_node.h"
34#include "parser/parse_oper.h"
36#include "parser/parsetree.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 */
58{
60 const char *variable;
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. */
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 */
80{
81 /* Path factor from which this element is derived. */
85 /* Source and destination vertex elements for an edge element. */
88 /* Source and destination conditions for an edge element. */
91};
92
93static Node *replace_property_refs(Oid propgraphid, Node *node, const List *mappings);
97static Node *generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist);
101static List *get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf);
103static 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 */
109Query *
110rewriteGraphTable(Query *parsetree, int rt_index)
111{
116
117 rte = rt_fetch(rt_index, parsetree->rtable);
118
119 Assert(list_length(rte->graph_pattern->path_pattern_list) == 1);
120
121 path_pattern = linitial(rte->graph_pattern->path_pattern_list);
124
126
127 rte->rtekind = RTE_SUBQUERY;
128 rte->subquery = graph_table_query;
129 rte->lateral = true;
130
131 /*
132 * Reset no longer applicable fields, to appease
133 * WRITE_READ_PARSE_PLAN_TREES.
134 */
135 rte->graph_pattern = NULL;
136 rte->graph_table_columns = NIL;
137
138 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 */
174static List *
176{
179 int factorpos = 0;
181 struct path_factor *prev_pf = NULL;
182
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 */
193 {
194 struct path_factor *pf = NULL;
195
196 /*
197 * Unsupported conditions should have been caught by the parser
198 * itself. We have corresponding Asserts here to document the
199 * assumptions in this code.
200 */
201 Assert(gep->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(gep->kind));
202 Assert(!gep->quantifier);
203
205 {
206 if (gep->variable && other->variable &&
207 strcmp(gep->variable, other->variable) == 0)
208 {
209 if (other->kind != gep->kind)
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 if (!other->labelexpr)
225 other->labelexpr = gep->labelexpr;
226 else if (gep->labelexpr && !equal(other->labelexpr, gep->labelexpr))
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 if (!other->whereClause)
238 other->whereClause = gep->whereClause;
239 else if (gep->whereClause)
240 other->whereClause = (Node *) makeBoolExpr(AND_EXPR,
241 list_make2(other->whereClause, gep->whereClause),
242 -1);
243 pf = other;
244 break;
245 }
246 }
247
248 if (!pf)
249 {
250 pf = palloc0_object(struct path_factor);
251 pf->factorpos = factorpos++;
252 pf->kind = gep->kind;
253 pf->labelexpr = gep->labelexpr;
254 pf->variable = gep->variable;
255 pf->whereClause = gep->whereClause;
256
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 */
284 if (prev_pf)
285 {
286 if (prev_pf->kind == EDGE_PATTERN_RIGHT || prev_pf->kind == EDGE_PATTERN_ANY)
287 {
288 Assert(!IS_EDGE_PATTERN(pf->kind));
289 if (prev_pf->dest_pf && prev_pf->dest_pf != pf)
292 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
293 prev_pf->dest_pf = pf;
294 }
295 else if (prev_pf->kind == EDGE_PATTERN_LEFT)
296 {
297 Assert(!IS_EDGE_PATTERN(pf->kind));
298 if (prev_pf->src_pf && prev_pf->src_pf != pf)
301 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
302 prev_pf->src_pf = pf;
303 }
304 else
305 {
306 Assert(prev_pf->kind == VERTEX_PATTERN);
307 Assert(IS_EDGE_PATTERN(pf->kind));
308 }
309
310 if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY)
311 {
313 if (pf->src_pf && pf->src_pf != prev_pf)
316 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
317 pf->src_pf = prev_pf;
318 }
319 else if (pf->kind == EDGE_PATTERN_LEFT)
320 {
322 if (pf->dest_pf && pf->dest_pf != prev_pf)
325 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
326 pf->dest_pf = prev_pf;
327 }
328 else
329 {
330 Assert(pf->kind == VERTEX_PATTERN);
332 }
333 }
334
335 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 */
345
347 NIL, path_elem_lists, 0);
348 if (!pathqueries)
350
351 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 */
360static List *
362{
364
365 /* Guard against stack overflow due to complex path patterns. */
367
369 {
370 /* Update current path being built with current element. */
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 */
378 {
380
382 if (pathquery)
384 }
385 else
387 cur_path,
389 elempos + 1);
390 /* Make way for the next element at the same position. */
392 }
393
394 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 */
418static Query *
420{
422 List *fromlist = NIL;
424 List *vars;
425
426 path_query->commandType = CMD_SELECT;
427
429 {
430 struct path_factor *pf = pe->path_factor;
432 Relation rel;
434
435 Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
436
437 /* Add conditions representing edge connections. */
438 if (IS_EDGE_PATTERN(pf->kind))
439 {
440 struct path_element *src_pe;
441 struct path_element *dest_pe;
443
444 Assert(pf->src_pf && pf->dest_pf);
445 src_pe = list_nth(graph_path, pf->src_pf->factorpos);
446 dest_pe = list_nth(graph_path, pf->dest_pf->factorpos);
447
448 /* Make sure that the links of adjacent vertices are correct. */
449 Assert(pf->src_pf == src_pe->path_factor &&
450 pf->dest_pf == dest_pe->path_factor);
451
452 if (src_pe->elemoid == pe->srcvertexid &&
453 dest_pe->elemoid == pe->destvertexid)
455 list_concat(copyObject(pe->src_quals),
456 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 if (pf->kind == EDGE_PATTERN_ANY &&
467 dest_pe->elemoid == pe->srcvertexid &&
468 src_pe->elemoid == pe->destvertexid)
469 {
470 List *src_quals = copyObject(pe->dest_quals);
471 List *dest_quals = copyObject(pe->src_quals);
473
474 /* Swap the source and destination varnos in the quals. */
475 ChangeVarNodes((Node *) dest_quals, pe->path_factor->src_pf->factorpos + 1,
476 pe->path_factor->dest_pf->factorpos + 1, 0);
477 ChangeVarNodes((Node *) src_quals, pe->path_factor->dest_pf->factorpos + 1,
478 pe->path_factor->src_pf->factorpos + 1, 0);
479
481 if (edge_qual)
483 else
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 if (edge_qual == NULL)
493 return NULL;
494
496 }
497 else
498 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 rel = table_open(pe->reloid, AccessShareLock);
512 NULL, true, false);
513 table_close(rel, NoLock);
514 path_query->rtable = lappend(path_query->rtable, pni->p_rte);
515 path_query->rteperminfos = lappend(path_query->rteperminfos, pni->p_perminfo);
516 pni->p_rte->perminfoindex = list_length(path_query->rteperminfos);
518 rtr->rtindex = list_length(path_query->rtable);
519 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 Assert(pf->factorpos + 1 == rtr->rtindex);
528
529 if (pf->whereClause)
530 {
531 Node *tr;
532
533 tr = replace_property_refs(rte->relid, pf->whereClause, list_make1(pe));
534
536 }
537 }
538
539 if (rte->graph_pattern->whereClause)
540 {
542 (Node *) rte->graph_pattern->whereClause,
543 graph_path);
544
546 }
547
548 path_query->jointree = makeFromExpr(fromlist,
550
551 /* Construct query targetlist from COLUMNS specification of GRAPH_TABLE. */
552 path_query->targetList = castNode(List,
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 */
563 foreach_node(Var, var, vars)
564 {
566 rt_fetch(var->varno, path_query->rtable));
567
568 /* Must offset the attnum to fit in a bitmapset */
569 perminfo->selectedCols = bms_add_member(perminfo->selectedCols,
570 var->varattno - FirstLowInvalidHeapAttributeNumber);
571 }
572
573 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 */
581static Query *
583{
584 Query *query = makeNode(Query);
585
586 query->commandType = CMD_SELECT;
587 query->rtable = NIL;
588 query->rteperminfos = NIL;
589 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 foreach_node(TargetEntry, te, rte->graph_table_columns)
597 {
598 Node *nte = (Node *) te->expr;
599
601 query->targetList = lappend(query->targetList, te);
602 }
603
604 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 */
619static Query *
621{
622 List *rtable = NIL;
626 int resno;
627 ListCell *lctl,
628 *lct,
629 *lcm,
630 *lcc;
631
633
634 /* If there's only one pathquery, no need to construct a UNION query. */
635 if (list_length(*pathqueries) == 1)
636 {
637 *pathqueries = NIL;
638 return sampleQuery;
639 }
640
643
644 /* Encapsulate the set operation statement into a Query. */
646 union_query->commandType = CMD_SELECT;
647 union_query->rtable = rtable;
648 union_query->setOperations = (Node *) sostmt;
649 union_query->rteperminfos = NIL;
650 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 Assert(rt_fetch(1, rtable));
662 union_query->targetList = NULL;
663 resno = 1;
664 forfour(lct, sostmt->colTypes,
665 lcm, sostmt->colTypmods,
666 lcc, sostmt->colCollations,
667 lctl, sampleQuery->targetList)
668 {
673 char *colName;
675 Var *var;
676
677 Assert(!sample_tle->resjunk);
678 colName = pstrdup(sample_tle->resname);
679 var = makeVar(1, sample_tle->resno, colType, colTypmod, colCollation, 0);
680 var->location = exprLocation((Node *) sample_tle->expr);
681 tle = makeTargetEntry((Expr *) var, (AttrNumber) resno++, colName, false);
682 union_query->targetList = lappend(union_query->targetList, tle);
683 }
684
685 *pathqueries = NIL;
686 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 */
695static Node *
697{
699 Query *lquery;
700 Node *rarg;
704
705 /* Guard against stack overflow due to many path queries. */
707
708 /* Recursion termination condition. */
709 if (list_length(pathqueries) == 0)
710 {
711 *targetlist = NIL;
712 return NULL;
713 }
714
716
718 false, false);
719 *rtable = lappend(*rtable, pni->p_rte);
720 lrtr->rtindex = list_length(*rtable);
722 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 if (targetlist)
730 {
731 *targetlist = NIL;
732 foreach_node(TargetEntry, tle, lquery->targetList)
733 {
734 if (!tle->resjunk)
735 *targetlist = lappend(*targetlist, tle);
736 }
737 }
738 return (Node *) lrtr;
739 }
740
742 sostmt->op = SETOP_UNION;
743 sostmt->all = true;
744 sostmt->larg = (Node *) lrtr;
745 sostmt->rarg = rarg;
746 constructSetOpTargetlist(NULL, sostmt, lquery->targetList, rtargetlist, targetlist, "UNION", false);
747
748 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 */
758static struct path_element *
760{
763 struct path_element *pe;
764
765 if (!eletup)
766 elog(ERROR, "cache lookup failed for property graph element %u", elemoid);
768
769 if ((pgeform->pgekind == PGEKIND_VERTEX && pf->kind != VERTEX_PATTERN) ||
770 (pgeform->pgekind == PGEKIND_EDGE && !IS_EDGE_PATTERN(pf->kind)))
771 {
773 return NULL;
774 }
775
777 pe->path_factor = pf;
778 pe->elemoid = elemoid;
779 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 if (IS_EDGE_PATTERN(pf->kind))
795 {
796 pe->srcvertexid = pgeform->pgesrcvertexid;
797 pe->destvertexid = pgeform->pgedestvertexid;
798 Assert(pf->src_pf && pf->dest_pf);
799
800 pe->src_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->src_pf->factorpos + 1,
801 pe->srcvertexid,
805 pe->dest_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->dest_pf->factorpos + 1,
806 pe->destvertexid,
810 }
811
813
814 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 */
821static List *
822get_labels_for_expr(Oid propgraphid, Node *labelexpr)
823{
825
826 if (!labelexpr)
827 {
828 Relation rel;
829 SysScanDesc scan;
830 ScanKeyData key[1];
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 label_oids = NIL;
841 ScanKeyInit(&key[0],
844 F_OIDEQ, ObjectIdGetDatum(propgraphid));
846 true, NULL, 1, key);
847 while (HeapTupleIsValid(tup = systable_getnext(scan)))
848 {
850
852 }
853 systable_endscan(scan);
855 }
856 else if (IsA(labelexpr, GraphLabelRef))
857 {
859
860 label_oids = list_make1_oid(glr->labelid);
861 }
862 else if (IsA(labelexpr, BoolExpr))
863 {
864 BoolExpr *be = castNode(BoolExpr, labelexpr);
865 List *label_exprs = be->args;
866
867 label_oids = NIL;
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 */
877 elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
878 }
879
880 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 */
899static List *
901{
902 List *label_oids = get_labels_for_expr(propgraphid, pf->labelexpr);
907 Relation rel;
908 SysScanDesc scan;
909 ScanKeyData key[1];
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 Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
918
921 {
922 bool found = false;
923
924 ScanKeyInit(&key[0],
929 NULL, 1, key);
930 while (HeapTupleIsValid(tup = systable_getnext(scan)))
931 {
933 Oid elem_oid = label_elem->pgelelid;
934
936 {
937 /*
938 * Create path_element object if the new element qualifies the
939 * element pattern kind.
940 */
942
943 if (pe)
944 {
946
947 /* Remember qualified elements. */
949 found = true;
950 }
951
952 /*
953 * Remember qualified and unqualified elements processed so
954 * far to avoid processing already processed elements again.
955 */
957 }
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 found = true;
966 }
967 }
968
969 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 if (!pf->labelexpr)
980 else
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",
986 get_rel_name(propgraphid))));
987 }
988
989 systable_endscan(scan);
990 }
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 */
1000
1001 return path_elements;
1002}
1003
1004/*
1005 * Mutating property references into table variables
1006 */
1007
1013
1014static Node *
1016{
1017 if (node == NULL)
1018 return NULL;
1019 if (IsA(node, Var))
1020 {
1021 Var *var = (Var *) node;
1022 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 newvar->varlevelsup++;
1030
1031 return (Node *) newvar;
1032 }
1033 else if (IsA(node, GraphPropertyRef))
1034 {
1036 Node *n = NULL;
1040
1041 foreach_ptr(struct path_element, m, context->mappings)
1042 {
1043 if (m->path_factor->variable && strcmp(gpr->elvarname, m->path_factor->variable) == 0)
1044 {
1045 found_mapping = m;
1046 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 */
1056
1057 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 */
1064 {
1069
1071 {
1073 ObjectIdGetDatum(gpr->propid));
1074
1075 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 continue;
1083 }
1084
1087 ChangeVarNodes(n, 1, mapping_factor->factorpos + 1, 0);
1088
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 */
1099 }
1100 }
1101
1102 /* See if we can resolve the property in some other way. */
1103 if (!n)
1104 {
1105 bool prop_associated = false;
1106
1108 {
1110 {
1111 prop_associated = true;
1112 break;
1113 }
1114 }
1115
1116 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 n = get_element_property_expr(found_mapping->elemoid, gpr->propid,
1127 mapping_factor->factorpos + 1);
1128
1129 if (!n)
1130 n = (Node *) makeNullConst(gpr->typeId, gpr->typmod, gpr->collation);
1131 }
1132
1133 }
1134
1135 if (!n)
1136 ereport(ERROR,
1138 errmsg("property \"%s\" for element variable \"%s\" not found",
1139 get_propgraph_property_name(gpr->propid), mapping_factor->variable));
1140
1141 return n;
1142 }
1143
1145}
1146
1147static Node *
1148replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
1149{
1150 struct replace_property_refs_context context;
1151
1152 context.mappings = mappings;
1153 context.propgraphid = propgraphid;
1154
1156}
1157
1158/*
1159 * Build join qualification expressions between edge and vertex tables.
1160 */
1161static List *
1163{
1164 List *quals = NIL;
1166 Datum datum;
1167 Datum *d1,
1168 *d2,
1169 *d3;
1170 int n1,
1171 n2,
1172 n3;
1173 ParseState *pstate = make_parsestate(NULL);
1175
1177
1180
1183
1186
1187 if (n1 != n2)
1188 elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
1189 if (n1 != n3)
1190 elog(ERROR, "array size key (%d) vs operator (%d) mismatch for element ID %u", catalog_key_attnum, catalog_eqop_attnum, pgeform->oid);
1191
1192 for (int i = 0; i < n1; i++)
1193 {
1196 Oid eqop = DatumGetObjectId(d3[i]);
1197 Var *keyvar;
1198 Var *refvar;
1199 Oid atttypid;
1200 int32 atttypmod;
1201 Oid attcoll;
1202 HeapTuple tup;
1204 List *args;
1208
1209 get_atttypetypmodcoll(pgeform->pgerelid, keyattn, &atttypid, &atttypmod, &attcoll);
1210 keyvar = makeVar(edgerti, keyattn, atttypid, atttypmod, attcoll, 0);
1211 get_atttypetypmodcoll(refrelid, refattn, &atttypid, &atttypmod, &attcoll);
1212 refvar = makeVar(refrti, refattn, atttypid, atttypmod, attcoll, 0);
1213
1215 if (!HeapTupleIsValid(tup))
1216 elog(ERROR, "cache lookup failed for operator %u", eqop);
1218 /* An equality operator is a binary operator returning boolean result. */
1219 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 args = list_make2(refvar, keyvar);
1233 declared_arg_types[0] = opform->oprleft;
1234 declared_arg_types[1] = opform->oprright;
1236
1238 linkqual->opno = opform->oid;
1239 linkqual->opfuncid = opform->oprcode;
1240 linkqual->opresulttype = opform->oprresult;
1241 linkqual->opretset = false;
1242 /* opcollid and inputcollid will be set by parse_collate.c */
1243 linkqual->args = args;
1244 linkqual->location = -1;
1245
1247 quals = lappend(quals, linkqual);
1248 }
1249
1250 assign_expr_collations(pstate, (Node *) quals);
1251
1252 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 */
1262static bool
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 */
1297static Node *
#define DatumGetArrayTypeP(X)
Definition array.h:261
void deconstruct_array_builtin(const ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
int16 AttrNumber
Definition attnum.h:21
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
#define TextDatumGetCString(d)
Definition builtins.h:99
#define RegProcedureIsValid(p)
Definition c.h:862
#define Assert(condition)
Definition c.h:943
int32_t int32
Definition c.h:620
#define OidIsValid(objectId)
Definition c.h:858
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
#define palloc0_object(type)
Definition fe_memutils.h:75
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:612
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:523
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
int i
Definition isn.c:77
List * lappend(List *list, void *datum)
Definition list.c:339
List * list_delete_first(List *list)
Definition list.c:943
List * list_difference_oid(const List *list1, const List *list2)
Definition list.c:1313
List * list_concat(List *list1, const List *list2)
Definition list.c:561
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
List * list_delete_last(List *list)
Definition list.c:957
bool list_member_oid(const List *list, Oid datum)
Definition list.c:722
#define NoLock
Definition lockdefs.h:34
#define AccessShareLock
Definition lockdefs.h:36
#define RowShareLock
Definition lockdefs.h:37
char * get_rel_name(Oid relid)
Definition lsyscache.c:2121
char * get_propgraph_property_name(Oid propoid)
Definition lsyscache.c:3957
bool get_func_retset(Oid funcid)
Definition lsyscache.c:1935
char * get_propgraph_label_name(Oid labeloid)
Definition lsyscache.c:3939
void get_atttypetypmodcoll(Oid relid, AttrNumber attnum, Oid *typid, int32 *typmod, Oid *collid)
Definition lsyscache.c:1062
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition makefuncs.c:420
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition makefuncs.c:336
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition makefuncs.c:66
Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
Definition makefuncs.c:388
Node * makeBoolConst(bool value, bool isnull)
Definition makefuncs.c:408
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition makefuncs.c:289
char * pstrdup(const char *in)
Definition mcxt.c:1781
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
int32 exprTypmod(const Node *expr)
Definition nodeFuncs.c:304
Oid exprCollation(const Node *expr)
Definition nodeFuncs.c:826
int exprLocation(const Node *expr)
Definition nodeFuncs.c:1392
#define expression_tree_mutator(n, m, c)
Definition nodeFuncs.h:155
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define copyObject(obj)
Definition nodes.h:232
#define nodeTag(nodeptr)
Definition nodes.h:139
@ CMD_SELECT
Definition nodes.h:275
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
static char * errmsg
void assign_expr_collations(ParseState *pstate, Node *expr)
void make_fn_arguments(ParseState *pstate, List *fargs, Oid *actual_arg_types, Oid *declared_arg_types)
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
RTEPermissionInfo * getRTEPermissionInfo(List *rteperminfos, RangeTblEntry *rte)
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, LOCKMODE lockmode, Alias *alias, bool inh, bool inFromCl)
ParseNamespaceItem * addRangeTableEntryForSubquery(ParseState *pstate, Query *subquery, Alias *alias, bool lateral, bool inFromCl)
@ SETOP_UNION
@ RTE_SUBQUERY
GraphElementPatternKind
@ EDGE_PATTERN_RIGHT
@ VERTEX_PATTERN
@ EDGE_PATTERN_LEFT
@ EDGE_PATTERN_ANY
#define IS_EDGE_PATTERN(kind)
void constructSetOpTargetlist(ParseState *pstate, SetOperationStmt *op, const List *ltargetlist, const List *rtargetlist, List **targetlist, const char *context, bool recursive)
Definition analyze.c:2609
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
static char * label
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152
#define linitial_node(type, l)
Definition pg_list.h:181
#define NIL
Definition pg_list.h:68
#define lfirst_int(lc)
Definition pg_list.h:173
#define list_make1_oid(x1)
Definition pg_list.h:274
#define list_make1(x1)
Definition pg_list.h:244
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
#define linitial(l)
Definition pg_list.h:178
#define foreach_node(type, var, lst)
Definition pg_list.h:528
#define forfour(cell1, list1, cell2, list2, cell3, list3, cell4, list4)
Definition pg_list.h:607
#define foreach_oid(var, lst)
Definition pg_list.h:503
#define list_nth_node(type, list, n)
Definition pg_list.h:359
#define lfirst_oid(lc)
Definition pg_list.h:174
#define list_make2(x1, x2)
Definition pg_list.h:246
END_CATALOG_STRUCT typedef FormData_pg_operator * Form_pg_operator
Definition pg_operator.h:87
END_CATALOG_STRUCT typedef FormData_pg_propgraph_element * Form_pg_propgraph_element
END_CATALOG_STRUCT typedef FormData_pg_propgraph_element_label * Form_pg_propgraph_element_label
END_CATALOG_STRUCT typedef FormData_pg_propgraph_label * Form_pg_propgraph_label
static Oid DatumGetObjectId(Datum X)
Definition postgres.h:242
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static int16 DatumGetInt16(Datum X)
Definition postgres.h:162
unsigned int Oid
static int fb(int x)
@ AND_EXPR
Definition primnodes.h:964
@ OR_EXPR
Definition primnodes.h:964
void * stringToNode(const char *str)
Definition read.c:90
static Query * generate_query_for_graph_path(RangeTblEntry *rte, List *graph_path)
static List * generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern)
static Node * replace_property_refs_mutator(Node *node, struct replace_property_refs_context *context)
static Node * get_element_property_expr(Oid elemoid, Oid propoid, int rtindex)
Query * rewriteGraphTable(Query *parsetree, int rt_index)
static Query * generate_union_from_pathqueries(List **pathqueries)
static struct path_element * create_pe_for_element(struct path_factor *pf, Oid elemoid)
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)
static List * generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, List *cur_path, List *path_elem_lists, int elempos)
static List * get_labels_for_expr(Oid propgraphid, Node *labelexpr)
static Node * replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
static Node * generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist)
static List * get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf)
static bool is_property_associated_with_label(Oid labeloid, Oid propoid)
static Query * generate_query_for_empty_path_pattern(RangeTblEntry *rte)
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
void check_stack_depth(void)
Definition stack_depth.c:95
#define BTEqualStrategyNumber
Definition stratnum.h:31
Definition pg_list.h:54
Definition nodes.h:135
FromExpr * jointree
Definition parsenodes.h:185
List * rtable
Definition parsenodes.h:178
CmdType commandType
Definition parsenodes.h:121
List * targetList
Definition parsenodes.h:201
ParseLoc location
Definition primnodes.h:311
struct path_factor * path_factor
struct path_factor * src_pf
struct path_factor * dest_pf
const char * variable
GraphElementPatternKind kind
#define FirstLowInvalidHeapAttributeNumber
Definition sysattr.h:27
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:231
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:626
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition syscache.h:102
#define GetSysCacheOid1(cacheId, oidcol, key1)
Definition syscache.h:109
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
Definition syscache.h:111
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
List * pull_vars_of_level(Node *node, int levelsup)
Definition var.c:339