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 "nodes/makefuncs.h"
27#include "nodes/nodeFuncs.h"
28#include "optimizer/optimizer.h"
29#include "parser/analyze.h"
31#include "parser/parse_func.h"
32#include "parser/parse_node.h"
33#include "parser/parse_oper.h"
35#include "parser/parsetree.h"
40#include "utils/array.h"
41#include "utils/builtins.h"
42#include "utils/fmgroids.h"
43#include "utils/lsyscache.h"
44#include "utils/ruleutils.h"
45#include "utils/syscache.h"
46
47
48/*
49 * Represents one path factor in a path.
50 *
51 * In a non-cyclic path, one path factor corresponds to one element pattern.
52 *
53 * In a cyclic path, one path factor corresponds to all the element patterns with
54 * the same variable name.
55 */
57{
59 const char *variable;
62 int factorpos; /* Position of this path factor in the list of
63 * path factors representing a given path
64 * pattern. */
65 List *labeloids; /* OIDs of all the labels referenced in
66 * labelexpr. */
67 /* Links to adjacent vertex path factors if this is an edge path factor. */
70};
71
72/*
73 * Represents one property graph element (vertex or edge) in the path.
74 *
75 * Label expression in an element pattern resolves into a set of elements. We
76 * create one path_element object for each of those elements.
77 */
79{
80 /* Path factor from which this element is derived. */
84 /* Source and destination vertex elements for an edge element. */
87 /* Source and destination conditions for an edge element. */
90};
91
92static Node *replace_property_refs(Oid propgraphid, Node *node, const List *mappings);
96static Node *generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetlist);
100static List *get_path_elements_for_path_factor(Oid propgraphid, struct path_factor *pf);
102static Node *get_element_property_expr(Oid elemoid, Oid propoid, int rtindex);
103
104/*
105 * Convert GRAPH_TABLE clause into a subquery using relational
106 * operators.
107 */
108Query *
109rewriteGraphTable(Query *parsetree, int rt_index)
110{
115
116 rte = rt_fetch(rt_index, parsetree->rtable);
117
118 Assert(list_length(rte->graph_pattern->path_pattern_list) == 1);
119
120 path_pattern = linitial(rte->graph_pattern->path_pattern_list);
123
125
126 rte->rtekind = RTE_SUBQUERY;
127 rte->subquery = graph_table_query;
128 rte->lateral = true;
129
130 /*
131 * Reset no longer applicable fields, to appease
132 * WRITE_READ_PARSE_PLAN_TREES.
133 */
134 rte->graph_pattern = NULL;
135 rte->graph_table_columns = NIL;
136
137 return parsetree;
138}
139
140/*
141 * Generate queries representing the given path pattern applied to the given
142 * property graph.
143 *
144 * A path pattern consists of one or more element patterns. Each of the element
145 * patterns may be satisfied by multiple elements. A path satisfying the given
146 * path pattern consists of one element from each element pattern. There can be
147 * as many paths as the number of combinations of the elements. A path pattern
148 * in itself is a K-partite graph where K = number of element patterns in the
149 * path pattern. The possible paths are computed by performing a DFS in this
150 * graph. The DFS is implemented as recursion. Each of these paths is converted
151 * into a query connecting all the elements in that path. Set of these queries is
152 * returned.
153 *
154 * Between every two vertex elements in the path there is an edge element that
155 * connects them. An edge connects two vertexes identified by the source and
156 * destination keys respectively. The connection between an edge and its
157 * adjacent vertex is naturally computed as an equi-join between edge and vertex
158 * table on their respective keys. Hence the query representing one path
159 * consists of JOINs between edge and vertex tables.
160 *
161 * generate_queries_for_path_pattern() starts the recursion but actual work is
162 * done by generate_queries_for_path_pattern_recurse().
163 * generate_query_for_graph_path() constructs a query for a given path.
164 *
165 * A path pattern may result into no path if any of the element pattern yields no
166 * elements or edge patterns yield no edges connecting adjacent vertex patterns.
167 * In such a case a dummy query which returns no result is returned
168 * (generate_query_for_empty_path_pattern()).
169 *
170 * 'path_pattern' is given path pattern to be applied on the property graph in
171 * the GRAPH_TABLE clause represented by given 'rte'.
172 */
173static List *
175{
178 int factorpos = 0;
180 struct path_factor *prev_pf = NULL;
181
183
184 /*
185 * Create a list of path factors representing the given path pattern
186 * linking edge path factors to their adjacent vertex path factors.
187 *
188 * While doing that merge element patterns with the same variable name
189 * into a single path_factor.
190 */
192 {
193 struct path_factor *pf = NULL;
194
195 /*
196 * Unsupported conditions should have been caught by the parser
197 * itself. We have corresponding Asserts here to document the
198 * assumptions in this code.
199 */
200 Assert(gep->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(gep->kind));
201 Assert(!gep->quantifier);
202
204 {
205 if (gep->variable && other->variable &&
206 strcmp(gep->variable, other->variable) == 0)
207 {
208 if (other->kind != gep->kind)
211 errmsg("element patterns with same variable name \"%s\" but different element pattern types",
212 gep->variable)));
213
214 /*
215 * If both the element patterns have label expressions, they
216 * need to be conjuncted, which is not supported right now.
217 *
218 * However, an empty label expression means all labels.
219 * Conjunction of any label expression with all labels is the
220 * expression itself. Hence if only one of the two element
221 * patterns has a label expression use that expression.
222 */
223 if (!other->labelexpr)
224 other->labelexpr = gep->labelexpr;
225 else if (gep->labelexpr && !equal(other->labelexpr, gep->labelexpr))
228 errmsg("element patterns with same variable name \"%s\" but different label expressions are not supported",
229 gep->variable)));
230
231 /*
232 * If two element patterns have the same variable name, they
233 * represent the same set of graph elements and hence are
234 * constrained by conditions from both the element patterns.
235 */
236 if (!other->whereClause)
237 other->whereClause = gep->whereClause;
238 else if (gep->whereClause)
239 other->whereClause = (Node *) makeBoolExpr(AND_EXPR,
240 list_make2(other->whereClause, gep->whereClause),
241 -1);
242 pf = other;
243 break;
244 }
245 }
246
247 if (!pf)
248 {
249 pf = palloc0_object(struct path_factor);
250 pf->factorpos = factorpos++;
251 pf->kind = gep->kind;
252 pf->labelexpr = gep->labelexpr;
253 pf->variable = gep->variable;
254 pf->whereClause = gep->whereClause;
255
257 }
258
259 /*
260 * Setup links to the previous path factor in the path.
261 *
262 * If the previous path factor represents an edge, this path factor
263 * represents an adjacent vertex; the source vertex for an edge
264 * pointing left or the destination vertex for an edge pointing right.
265 * If this path factor represents an edge, the previous path factor
266 * represents an adjacent vertex; source vertex for an edge pointing
267 * right or the destination vertex for an edge pointing left.
268 *
269 * Edge pointing in any direction is treated similar to that pointing
270 * in right direction here. When constructing a query in
271 * generate_query_for_graph_path(), we will try links in both the
272 * directions.
273 *
274 * If multiple edge patterns share the same variable name, they
275 * constrain the adjacent vertex patterns since an edge can connect
276 * only one pair of vertexes. These adjacent vertex patterns need to
277 * be merged even though they have different variables. Such element
278 * patterns form a walk of graph where vertex and edges are repeated.
279 * For example, in (a)-[b]->(c)<-[b]-(d), (a) and (d) represent the
280 * same vertex element. This is slightly harder to implement and
281 * probably less useful. Hence not supported for now.
282 */
283 if (prev_pf)
284 {
285 if (prev_pf->kind == EDGE_PATTERN_RIGHT || prev_pf->kind == EDGE_PATTERN_ANY)
286 {
287 Assert(!IS_EDGE_PATTERN(pf->kind));
288 if (prev_pf->dest_pf && prev_pf->dest_pf != pf)
291 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
292 prev_pf->dest_pf = pf;
293 }
294 else if (prev_pf->kind == EDGE_PATTERN_LEFT)
295 {
296 Assert(!IS_EDGE_PATTERN(pf->kind));
297 if (prev_pf->src_pf && prev_pf->src_pf != pf)
300 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
301 prev_pf->src_pf = pf;
302 }
303 else
304 {
305 Assert(prev_pf->kind == VERTEX_PATTERN);
306 Assert(IS_EDGE_PATTERN(pf->kind));
307 }
308
309 if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY)
310 {
312 if (pf->src_pf && pf->src_pf != prev_pf)
315 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
316 pf->src_pf = prev_pf;
317 }
318 else if (pf->kind == EDGE_PATTERN_LEFT)
319 {
321 if (pf->dest_pf && pf->dest_pf != prev_pf)
324 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
325 pf->dest_pf = prev_pf;
326 }
327 else
328 {
329 Assert(pf->kind == VERTEX_PATTERN);
331 }
332 }
333
334 prev_pf = pf;
335 }
336
337 /*
338 * Collect list of elements for each path factor. Do this after all the
339 * edge links are setup correctly.
340 */
344
346 NIL, path_elem_lists, 0);
347 if (!pathqueries)
349
350 return pathqueries;
351}
352
353/*
354 * Recursive workhorse function of generate_queries_for_path_pattern().
355 *
356 * `elempos` is the position of the next element being added in the path being
357 * built.
358 */
359static List *
361{
363
365 {
366 /* Update current path being built with current element. */
368
369 /*
370 * If this is the last element in the path, generate query for the
371 * completed path. Else recurse processing the next element.
372 */
374 {
376
378 if (pathquery)
380 }
381 else
383 cur_path,
385 elempos + 1);
386 /* Make way for the next element at the same position. */
388 }
389
390 return pathqueries;
391}
392
393/*
394 * Construct a query representing given graph path.
395 *
396 * The query contains:
397 *
398 * 1. targetlist corresponding to the COLUMNS clause of GRAPH_TABLE clause
399 *
400 * 2. quals corresponding to the WHERE clause of individual elements, WHERE
401 * clause in GRAPH_TABLE clause and quals representing edge-vertex links.
402 *
403 * 3. fromlist containing all elements in the path
404 *
405 * The collations of property expressions are obtained from the catalog. The
406 * collations of expressions in COLUMNS and WHERE clauses are assigned before
407 * rewriting the graph table. The collations of the edge-vertex link quals are
408 * assigned when crafting those quals. Thus everything in the query that requires
409 * collation assignment has been taken care of already. No separate collation
410 * assignment is required in this function.
411 *
412 * More details in the prologue of generate_queries_for_path_pattern().
413 */
414static Query *
416{
418 List *fromlist = NIL;
420 List *vars;
421
422 path_query->commandType = CMD_SELECT;
423
425 {
426 struct path_factor *pf = pe->path_factor;
428 Relation rel;
430
431 Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
432
433 /* Add conditions representing edge connections. */
434 if (IS_EDGE_PATTERN(pf->kind))
435 {
436 struct path_element *src_pe;
437 struct path_element *dest_pe;
439
440 Assert(pf->src_pf && pf->dest_pf);
441 src_pe = list_nth(graph_path, pf->src_pf->factorpos);
442 dest_pe = list_nth(graph_path, pf->dest_pf->factorpos);
443
444 /* Make sure that the links of adjacent vertices are correct. */
445 Assert(pf->src_pf == src_pe->path_factor &&
446 pf->dest_pf == dest_pe->path_factor);
447
448 if (src_pe->elemoid == pe->srcvertexid &&
449 dest_pe->elemoid == pe->destvertexid)
451 list_concat(copyObject(pe->src_quals),
452 copyObject(pe->dest_quals)),
453 -1);
454
455 /*
456 * An edge pattern in any direction matches edges in both
457 * directions, try swapping source and destination. When the
458 * source and destination is the same vertex table, quals
459 * corresponding to either direction may get satisfied. Hence OR
460 * the quals corresponding to both the directions.
461 */
462 if (pf->kind == EDGE_PATTERN_ANY &&
463 dest_pe->elemoid == pe->srcvertexid &&
464 src_pe->elemoid == pe->destvertexid)
465 {
466 List *src_quals = copyObject(pe->dest_quals);
467 List *dest_quals = copyObject(pe->src_quals);
469
470 /* Swap the source and destination varnos in the quals. */
471 ChangeVarNodes((Node *) dest_quals, pe->path_factor->src_pf->factorpos + 1,
472 pe->path_factor->dest_pf->factorpos + 1, 0);
473 ChangeVarNodes((Node *) src_quals, pe->path_factor->dest_pf->factorpos + 1,
474 pe->path_factor->src_pf->factorpos + 1, 0);
475
477 if (edge_qual)
479 else
481 }
482
483 /*
484 * If the given edge element does not connect the adjacent vertex
485 * elements in this path, the path is broken. Abandon this path as
486 * it won't return any rows.
487 */
488 if (edge_qual == NULL)
489 return NULL;
490
492 }
493 else
494 Assert(!pe->src_quals && !pe->dest_quals);
495
496 /*
497 * Create RangeTblEntry for this element table.
498 *
499 * SQL/PGQ standard (Ref. Section 11.19, Access rule 2 and General
500 * rule 4) does not specify whose access privileges to use when
501 * accessing the element tables: property graph owner's or current
502 * user's. It is safer to use current user's privileges so as not to
503 * make property graphs as a hole for unpriviledged data access. This
504 * is inline with the views being security_invoker by default.
505 */
506 rel = table_open(pe->reloid, AccessShareLock);
508 NULL, true, false);
509 table_close(rel, NoLock);
510 path_query->rtable = lappend(path_query->rtable, pni->p_rte);
511 path_query->rteperminfos = lappend(path_query->rteperminfos, pni->p_perminfo);
512 pni->p_rte->perminfoindex = list_length(path_query->rteperminfos);
514 rtr->rtindex = list_length(path_query->rtable);
515 fromlist = lappend(fromlist, rtr);
516
517 /*
518 * Make sure that the assumption mentioned in create_pe_for_element()
519 * holds true; that the elements' RangeTblEntrys are added in the
520 * order in which their respective path factors appear in the list of
521 * path factors representing the path pattern.
522 */
523 Assert(pf->factorpos + 1 == rtr->rtindex);
524
525 if (pf->whereClause)
526 {
527 Node *tr;
528
529 tr = replace_property_refs(rte->relid, pf->whereClause, list_make1(pe));
530
532 }
533 }
534
535 if (rte->graph_pattern->whereClause)
536 {
538 (Node *) rte->graph_pattern->whereClause,
539 graph_path);
540
542 }
543
544 path_query->jointree = makeFromExpr(fromlist,
546
547 /* Construct query targetlist from COLUMNS specification of GRAPH_TABLE. */
548 path_query->targetList = castNode(List,
550 (Node *) rte->graph_table_columns,
551 graph_path));
552
553 /*
554 * Mark the columns being accessed in the path query as requiring SELECT
555 * privilege. Any lateral columns should have been handled when the
556 * corresponding ColumnRefs were transformed. Ignore those here.
557 */
559 foreach_node(Var, var, vars)
560 {
562 rt_fetch(var->varno, path_query->rtable));
563
564 /* Must offset the attnum to fit in a bitmapset */
565 perminfo->selectedCols = bms_add_member(perminfo->selectedCols,
566 var->varattno - FirstLowInvalidHeapAttributeNumber);
567 }
568
569 return path_query;
570}
571
572/*
573 * Construct a query which would not return any rows.
574 *
575 * More details in the prologue of generate_queries_for_path_pattern().
576 */
577static Query *
579{
580 Query *query = makeNode(Query);
581
582 query->commandType = CMD_SELECT;
583 query->rtable = NIL;
584 query->rteperminfos = NIL;
585 query->jointree = makeFromExpr(NIL, (Node *) makeBoolConst(false, false));
586
587 /*
588 * Even though no rows are returned, the result still projects the same
589 * columns as projected by GRAPH_TABLE clause. Do this by constructing a
590 * target list full of NULL values.
591 */
592 foreach_node(TargetEntry, te, rte->graph_table_columns)
593 {
594 Node *nte = (Node *) te->expr;
595
597 query->targetList = lappend(query->targetList, te);
598 }
599
600 return query;
601}
602
603/*
604 * Construct a query which is UNION of given path queries.
605 *
606 * The UNION query derives collations of its targetlist entries from the
607 * corresponding targetlist entries of the path queries. The targetlists of path
608 * queries being UNION'ed already have collations assigned. No separate
609 * collation assignment required in this function.
610 *
611 * The function destroys given pathqueries list while constructing
612 * SetOperationStmt recursively. Hence the function always returns with
613 * `pathqueries` set to NIL.
614 */
615static Query *
617{
618 List *rtable = NIL;
622 int resno;
623 ListCell *lctl,
624 *lct,
625 *lcm,
626 *lcc;
627
629
630 /* If there's only one pathquery, no need to construct a UNION query. */
631 if (list_length(*pathqueries) == 1)
632 {
633 *pathqueries = NIL;
634 return sampleQuery;
635 }
636
639
640 /* Encapsulate the set operation statement into a Query. */
642 union_query->commandType = CMD_SELECT;
643 union_query->rtable = rtable;
644 union_query->setOperations = (Node *) sostmt;
645 union_query->rteperminfos = NIL;
646 union_query->jointree = makeFromExpr(NIL, NULL);
647
648 /*
649 * Generate dummy targetlist for outer query using column names from one
650 * of the queries and common datatypes/collations of topmost set
651 * operation. It shouldn't matter which query. Also it shouldn't matter
652 * which RT index is used as varno in the target list entries, as long as
653 * it corresponds to a real RT entry; else funny things may happen when
654 * the tree is mashed by rule rewriting. So we use 1 since there's always
655 * one RT entry at least.
656 */
657 Assert(rt_fetch(1, rtable));
658 union_query->targetList = NULL;
659 resno = 1;
660 forfour(lct, sostmt->colTypes,
661 lcm, sostmt->colTypmods,
662 lcc, sostmt->colCollations,
663 lctl, sampleQuery->targetList)
664 {
669 char *colName;
671 Var *var;
672
673 Assert(!sample_tle->resjunk);
674 colName = pstrdup(sample_tle->resname);
675 var = makeVar(1, sample_tle->resno, colType, colTypmod, colCollation, 0);
676 var->location = exprLocation((Node *) sample_tle->expr);
677 tle = makeTargetEntry((Expr *) var, (AttrNumber) resno++, colName, false);
678 union_query->targetList = lappend(union_query->targetList, tle);
679 }
680
681 *pathqueries = NIL;
682 return union_query;
683}
684
685/*
686 * Construct a query which is UNION of all the given path queries.
687 *
688 * The function destroys given pathqueries list while constructing
689 * SetOperationStmt recursively.
690 */
691static Node *
693{
695 Query *lquery;
696 Node *rarg;
700
701 /* Recursion termination condition. */
702 if (list_length(pathqueries) == 0)
703 {
704 *targetlist = NIL;
705 return NULL;
706 }
707
709
711 false, false);
712 *rtable = lappend(*rtable, pni->p_rte);
713 lrtr->rtindex = list_length(*rtable);
715 if (rarg == NULL)
716 {
717 /*
718 * No further path queries in the list. Convert the last query into a
719 * RangeTblRef as expected by SetOperationStmt. Extract a list of the
720 * non-junk TLEs for upper-level processing.
721 */
722 if (targetlist)
723 {
724 *targetlist = NIL;
725 foreach_node(TargetEntry, tle, lquery->targetList)
726 {
727 if (!tle->resjunk)
728 *targetlist = lappend(*targetlist, tle);
729 }
730 }
731 return (Node *) lrtr;
732 }
733
735 sostmt->op = SETOP_UNION;
736 sostmt->all = true;
737 sostmt->larg = (Node *) lrtr;
738 sostmt->rarg = rarg;
739 constructSetOpTargetlist(NULL, sostmt, lquery->targetList, rtargetlist, targetlist, "UNION", false);
740
741 return (Node *) sostmt;
742}
743
744/*
745 * Construct a path_element object for the graph element given by `elemoid`
746 * satisfied by the path factor `pf`.
747 *
748 * If the type of graph element does not fit the element pattern kind, the
749 * function returns NULL.
750 */
751static struct path_element *
753{
756 struct path_element *pe;
757
758 if (!eletup)
759 elog(ERROR, "cache lookup failed for property graph element %u", elemoid);
761
762 if ((pgeform->pgekind == PGEKIND_VERTEX && pf->kind != VERTEX_PATTERN) ||
763 (pgeform->pgekind == PGEKIND_EDGE && !IS_EDGE_PATTERN(pf->kind)))
764 {
766 return NULL;
767 }
768
770 pe->path_factor = pf;
771 pe->elemoid = elemoid;
772 pe->reloid = pgeform->pgerelid;
773
774 /*
775 * When a path is converted into a query
776 * (generate_query_for_graph_path()), a RangeTblEntry will be created for
777 * every element in the path. Fixing rtindexes of RangeTblEntrys here
778 * makes it possible to craft elements' qual expressions only once while
779 * we have access to the catalog entry. Otherwise they need to be crafted
780 * as many times as the number of paths a given element appears in,
781 * fetching catalog entry again each time. Hence we simply assume
782 * RangeTblEntrys will be created in the same order in which the
783 * corresponding path factors appear in the list of path factors
784 * representing a path pattern. That way their rtindexes will be same as
785 * path_factor::factorpos + 1.
786 */
787 if (IS_EDGE_PATTERN(pf->kind))
788 {
789 pe->srcvertexid = pgeform->pgesrcvertexid;
790 pe->destvertexid = pgeform->pgedestvertexid;
791 Assert(pf->src_pf && pf->dest_pf);
792
793 pe->src_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->src_pf->factorpos + 1,
794 pe->srcvertexid,
798 pe->dest_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->dest_pf->factorpos + 1,
799 pe->destvertexid,
803 }
804
806
807 return pe;
808}
809
810/*
811 * Returns the list of OIDs of graph labels which the given label expression
812 * resolves to in the given property graph.
813 */
814static List *
815get_labels_for_expr(Oid propgraphid, Node *labelexpr)
816{
818
819 if (!labelexpr)
820 {
821 Relation rel;
822 SysScanDesc scan;
823 ScanKeyData key[1];
825
826 /*
827 * According to section 9.2 "Contextual inference of a set of labels"
828 * subclause 2.a.ii of SQL/PGQ standard, element pattern which does
829 * not have a label expression is considered to have label expression
830 * equivalent to '%|!%' which is set of all labels.
831 */
832 label_oids = NIL;
834 ScanKeyInit(&key[0],
837 F_OIDEQ, ObjectIdGetDatum(propgraphid));
839 true, NULL, 1, key);
840 while (HeapTupleIsValid(tup = systable_getnext(scan)))
841 {
843
845 }
846 systable_endscan(scan);
848 }
849 else if (IsA(labelexpr, GraphLabelRef))
850 {
852
853 label_oids = list_make1_oid(glr->labelid);
854 }
855 else if (IsA(labelexpr, BoolExpr))
856 {
857 BoolExpr *be = castNode(BoolExpr, labelexpr);
858 List *label_exprs = be->args;
859
860 label_oids = NIL;
863 }
864 else
865 {
866 /*
867 * should not reach here since gram.y will not generate a label
868 * expression with other node types.
869 */
870 elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
871 }
872
873 return label_oids;
874}
875
876/*
877 * Return a list of all the graph elements that satisfy the graph element pattern
878 * represented by the given path_factor `pf`.
879 *
880 * First we find all the graph labels that satisfy the label expression in path
881 * factor. Each label is associated with one or more graph elements. A union of
882 * all such elements satisfies the element pattern. We create one path_element
883 * object representing every element whose graph element kind qualifies the
884 * element pattern kind. A list of all such path_element objects is returned.
885 *
886 * Note that we need to report an error for an explicitly specified label which
887 * is not associated with any graph element of the required kind. So we have to
888 * treat each label separately. Without that requirement we could have collected
889 * all the unique elements first and then created path_element objects for them
890 * to simplify the code.
891 */
892static List *
894{
895 List *label_oids = get_labels_for_expr(propgraphid, pf->labelexpr);
900 Relation rel;
901 SysScanDesc scan;
902 ScanKeyData key[1];
904
905 /*
906 * A property graph element can be either a vertex or an edge. Other types
907 * of path factors like nested path pattern need to be handled separately
908 * when supported.
909 */
910 Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
911
914 {
915 bool found = false;
916
917 ScanKeyInit(&key[0],
922 NULL, 1, key);
923 while (HeapTupleIsValid(tup = systable_getnext(scan)))
924 {
926 Oid elem_oid = label_elem->pgelelid;
927
929 {
930 /*
931 * Create path_element object if the new element qualifies the
932 * element pattern kind.
933 */
935
936 if (pe)
937 {
939
940 /* Remember qualified elements. */
942 found = true;
943 }
944
945 /*
946 * Remember qualified and unqualified elements processed so
947 * far to avoid processing already processed elements again.
948 */
950 }
952 {
953 /*
954 * The graph element is known to qualify the given element
955 * pattern. Flag that the current label has at least one
956 * qualified element associated with it.
957 */
958 found = true;
959 }
960 }
961
962 if (!found)
963 {
964 /*
965 * We did not find any qualified element associated with this
966 * label. The label or its properties can not be associated with
967 * the given element pattern. Throw an error if the label was
968 * explicitly specified in the element pattern. Otherwise remember
969 * it for later use.
970 */
971 if (!pf->labelexpr)
973 else
976 errmsg("no property graph element of type \"%s\" has label \"%s\" associated with it in property graph \"%s\"",
977 pf->kind == VERTEX_PATTERN ? "vertex" : "edge",
979 get_rel_name(propgraphid))));
980 }
981
982 systable_endscan(scan);
983 }
985
986 /*
987 * Remove the labels which were not explicitly mentioned in the label
988 * expression but do not have any qualified elements associated with them.
989 * Properties associated with such labels may not be referenced. See
990 * replace_property_refs_mutator() for more details.
991 */
993
994 return path_elements;
995}
996
997/*
998 * Mutating property references into table variables
999 */
1000
1006
1007static Node *
1009{
1010 if (node == NULL)
1011 return NULL;
1012 if (IsA(node, Var))
1013 {
1014 Var *var = (Var *) node;
1015 Var *newvar = copyObject(var);
1016
1017 /*
1018 * If it's already a Var, then it was a lateral reference. Since we
1019 * are in a subquery after the rewrite, we have to increase the level
1020 * by one.
1021 */
1022 newvar->varlevelsup++;
1023
1024 return (Node *) newvar;
1025 }
1026 else if (IsA(node, GraphPropertyRef))
1027 {
1029 Node *n = NULL;
1033
1034 foreach_ptr(struct path_element, m, context->mappings)
1035 {
1036 if (m->path_factor->variable && strcmp(gpr->elvarname, m->path_factor->variable) == 0)
1037 {
1038 found_mapping = m;
1039 break;
1040 }
1041 }
1042
1043 /*
1044 * transformGraphTablePropertyRef() would not create a
1045 * GraphPropertyRef for a variable which is not present in the graph
1046 * path pattern.
1047 */
1049
1050 mapping_factor = found_mapping->path_factor;
1051
1052 /*
1053 * Find property definition for given element through any of the
1054 * associated labels qualifying the given element pattern.
1055 */
1057 {
1062
1064 {
1066 ObjectIdGetDatum(gpr->propid));
1067
1068 if (!tup)
1069 {
1070 /*
1071 * The label is associated with the given element but it
1072 * is not associated with the required property. Check
1073 * next label.
1074 */
1075 continue;
1076 }
1077
1080 ChangeVarNodes(n, 1, mapping_factor->factorpos + 1, 0);
1081
1083 }
1084 else
1085 {
1086 /*
1087 * Label is not associated with the element but it may be
1088 * associated with the property through some other element.
1089 * Save it for later use.
1090 */
1092 }
1093 }
1094
1095 /* See if we can resolve the property in some other way. */
1096 if (!n)
1097 {
1098 bool prop_associated = false;
1099
1101 {
1103 {
1104 prop_associated = true;
1105 break;
1106 }
1107 }
1108
1109 if (prop_associated)
1110 {
1111 /*
1112 * The property is associated with at least one of the labels
1113 * that satisfy given element pattern. If it's associated with
1114 * the given element (through some other label), use
1115 * corresponding value expression. Otherwise NULL. Ref.
1116 * SQL/PGQ standard section 6.5 Property Reference, General
1117 * Rule 2.b.
1118 */
1119 n = get_element_property_expr(found_mapping->elemoid, gpr->propid,
1120 mapping_factor->factorpos + 1);
1121
1122 if (!n)
1123 n = (Node *) makeNullConst(gpr->typeId, gpr->typmod, gpr->collation);
1124 }
1125
1126 }
1127
1128 if (!n)
1129 ereport(ERROR,
1131 errmsg("property \"%s\" for element variable \"%s\" not found",
1132 get_propgraph_property_name(gpr->propid), mapping_factor->variable));
1133
1134 return n;
1135 }
1136
1138}
1139
1140static Node *
1141replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
1142{
1143 struct replace_property_refs_context context;
1144
1145 context.mappings = mappings;
1146 context.propgraphid = propgraphid;
1147
1149}
1150
1151/*
1152 * Build join qualification expressions between edge and vertex tables.
1153 */
1154static List *
1156{
1157 List *quals = NIL;
1159 Datum datum;
1160 Datum *d1,
1161 *d2,
1162 *d3;
1163 int n1,
1164 n2,
1165 n3;
1166 ParseState *pstate = make_parsestate(NULL);
1168
1170
1173
1176
1179
1180 if (n1 != n2)
1181 elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
1182 if (n1 != n3)
1183 elog(ERROR, "array size key (%d) vs operator (%d) mismatch for element ID %u", catalog_key_attnum, catalog_eqop_attnum, pgeform->oid);
1184
1185 for (int i = 0; i < n1; i++)
1186 {
1189 Oid eqop = DatumGetObjectId(d3[i]);
1190 Var *keyvar;
1191 Var *refvar;
1192 Oid atttypid;
1193 int32 atttypmod;
1194 Oid attcoll;
1195 HeapTuple tup;
1197 List *args;
1201
1202 get_atttypetypmodcoll(pgeform->pgerelid, keyattn, &atttypid, &atttypmod, &attcoll);
1203 keyvar = makeVar(edgerti, keyattn, atttypid, atttypmod, attcoll, 0);
1204 get_atttypetypmodcoll(refrelid, refattn, &atttypid, &atttypmod, &attcoll);
1205 refvar = makeVar(refrti, refattn, atttypid, atttypmod, attcoll, 0);
1206
1208 if (!HeapTupleIsValid(tup))
1209 elog(ERROR, "cache lookup failed for operator %u", eqop);
1211 /* An equality operator is a binary operator returning boolean result. */
1212 Assert(opform->oprkind == 'b'
1213 && RegProcedureIsValid(opform->oprcode)
1214 && opform->oprresult == BOOLOID
1215 && !get_func_retset(opform->oprcode));
1216
1217 /*
1218 * Prepare operands and cast them to the types required by the
1219 * equality operator. Similar to PK/FK quals, referenced vertex key is
1220 * used as left operand and referencing edge key is used as right
1221 * operand.
1222 */
1223 args = list_make2(refvar, keyvar);
1226 declared_arg_types[0] = opform->oprleft;
1227 declared_arg_types[1] = opform->oprright;
1229
1231 linkqual->opno = opform->oid;
1232 linkqual->opfuncid = opform->oprcode;
1233 linkqual->opresulttype = opform->oprresult;
1234 linkqual->opretset = false;
1235 /* opcollid and inputcollid will be set by parse_collate.c */
1236 linkqual->args = args;
1237 linkqual->location = -1;
1238
1240 quals = lappend(quals, linkqual);
1241 }
1242
1243 assign_expr_collations(pstate, (Node *) quals);
1244
1245 return quals;
1246}
1247
1248/*
1249 * Check if the given property is associated with the given label.
1250 *
1251 * A label projects the same set of properties through every element it is
1252 * associated with. Find any of the elements and return true if that element is
1253 * associated with the given property. False otherwise.
1254 */
1255static bool
1284
1285/*
1286 * If given element has the given property associated with it, through any of
1287 * the associated labels, return value expression of the property. Otherwise
1288 * NULL.
1289 */
1290static 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:874
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:227
#define ereport(elevel,...)
Definition elog.h:151
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:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
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:2148
char * get_propgraph_property_name(Oid propoid)
Definition lsyscache.c:3984
bool get_func_retset(Oid funcid)
Definition lsyscache.c:1962
char * get_propgraph_label_name(Oid labeloid)
Definition lsyscache.c:3966
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:2601
#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 List * generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries, List *cur_path, List *path_pattern_lists, int elempos)
static Query * generate_query_for_graph_path(RangeTblEntry *rte, List *path)
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 * get_labels_for_expr(Oid propgraphid, Node *labelexpr)
static List * generate_queries_for_path_pattern(RangeTblEntry *rte, List *element_patterns)
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
#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