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"
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 result into no path if any of the element pattern yields no
167 * elements or edge patterns yield no edges connecting adjacent vertex patterns.
168 * In such a case a dummy query which returns no result is returned
169 * (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 {
251 pf = palloc0_object(struct path_factor);
252 pf->factorpos = factorpos++;
253 pf->kind = gep->kind;
254 pf->labelexpr = gep->labelexpr;
255 pf->variable = gep->variable;
256 pf->whereClause = gep->whereClause;
257
259 }
260 }
261
262 /*
263 * Setup links to the previous path factor in the path.
264 *
265 * If the previous path factor represents an edge, this path factor
266 * represents an adjacent vertex; the source vertex for an edge
267 * pointing left or the destination vertex for an edge pointing right.
268 * If this path factor represents an edge, the previous path factor
269 * represents an adjacent vertex; source vertex for an edge pointing
270 * right or the destination vertex for an edge pointing left.
271 *
272 * Edge pointing in any direction is treated similar to that pointing
273 * in right direction here. When constructing a query in
274 * generate_query_for_graph_path(), we will try links in both the
275 * directions.
276 *
277 * If multiple edge patterns share the same variable name, they
278 * constrain the adjacent vertex patterns since an edge can connect
279 * only one pair of vertexes. These adjacent vertex patterns need to
280 * be merged even though they have different variables. Such element
281 * patterns form a walk of graph where vertex and edges are repeated.
282 * For example, in (a)-[b]->(c)<-[b]-(d), (a) and (d) represent the
283 * same vertex element. This is slighly harder to implement and
284 * probably less useful. Hence not supported for now.
285 */
286 if (prev_pf)
287 {
288 if (prev_pf->kind == EDGE_PATTERN_RIGHT || prev_pf->kind == EDGE_PATTERN_ANY)
289 {
290 Assert(!IS_EDGE_PATTERN(pf->kind));
291 if (prev_pf->dest_pf && prev_pf->dest_pf != pf)
294 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
295 prev_pf->dest_pf = pf;
296 }
297 else if (prev_pf->kind == EDGE_PATTERN_LEFT)
298 {
299 Assert(!IS_EDGE_PATTERN(pf->kind));
300 if (prev_pf->src_pf && prev_pf->src_pf != pf)
303 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
304 prev_pf->src_pf = pf;
305 }
306
307 if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY)
308 {
310 if (pf->src_pf && pf->src_pf != prev_pf)
313 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
314 pf->src_pf = prev_pf;
315 }
316 else if (pf->kind == EDGE_PATTERN_LEFT)
317 {
319 if (pf->dest_pf && pf->dest_pf != prev_pf)
322 errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern"));
323 pf->dest_pf = prev_pf;
324 }
325 }
326
327 prev_pf = pf;
328 }
329
330 /*
331 * Collect list of elements for each path factor. Do this after all the
332 * edge links are setup correctly.
333 */
337
339 NIL, path_elem_lists, 0);
340 if (!pathqueries)
342
343 return pathqueries;
344}
345
346/*
347 * Recursive workhorse function of generate_queries_for_path_pattern().
348 *
349 * `elempos` is the position of the next element being added in the path being
350 * built.
351 */
352static List *
354{
356
358 {
359 /* Update current path being built with current element. */
361
362 /*
363 * If this is the last element in the path, generate query for the
364 * completed path. Else recurse processing the next element.
365 */
367 {
369
371 if (pathquery)
373 }
374 else
376 cur_path,
378 elempos + 1);
379 /* Make way for the next element at the same position. */
381 }
382
383 return pathqueries;
384}
385
386/*
387 * Construct a query representing given graph path.
388 *
389 * The query contains:
390 *
391 * 1. targetlist corresponding to the COLUMNS clause of GRAPH_TABLE clause
392 *
393 * 2. quals corresponding to the WHERE clause of individual elements, WHERE
394 * clause in GRAPH_TABLE clause and quals representing edge-vertex links.
395 *
396 * 3. fromlist containing all elements in the path
397 *
398 * The collations of property expressions are obtained from the catalog. The
399 * collations of expressions in COLUMNS and WHERE clauses are assigned before
400 * rewriting the graph table. The collations of the edge-vertex link quals are
401 * assigned when crafting those quals. Thus everything in the query that requires
402 * collation assignment has been taken care of already. No separate collation
403 * assignment is required in this function.
404 *
405 * More details in the prologue of generate_queries_for_path_pattern().
406 */
407static Query *
409{
411 List *fromlist = NIL;
413 List *vars;
414
415 path_query->commandType = CMD_SELECT;
416
418 {
419 struct path_factor *pf = pe->path_factor;
421 Relation rel;
423
424 Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
425
426 /* Add conditions representing edge connnections. */
427 if (IS_EDGE_PATTERN(pf->kind))
428 {
429 struct path_element *src_pe;
430 struct path_element *dest_pe;
432
433 Assert(pf->src_pf && pf->dest_pf);
434 src_pe = list_nth(graph_path, pf->src_pf->factorpos);
435 dest_pe = list_nth(graph_path, pf->dest_pf->factorpos);
436
437 /* Make sure that the links of adjacent vertices are correct. */
438 Assert(pf->src_pf == src_pe->path_factor &&
439 pf->dest_pf == dest_pe->path_factor);
440
441 if (src_pe->elemoid == pe->srcvertexid &&
442 dest_pe->elemoid == pe->destvertexid)
444 list_concat(copyObject(pe->src_quals),
445 copyObject(pe->dest_quals)),
446 -1);
447
448 /*
449 * An edge pattern in any direction matches edges in both
450 * directions, try swapping source and destination. When the
451 * source and destination is the same vertex table, quals
452 * corresponding to either direction may get satisfied. Hence OR
453 * the quals corresponding to both the directions.
454 */
455 if (pf->kind == EDGE_PATTERN_ANY &&
456 dest_pe->elemoid == pe->srcvertexid &&
457 src_pe->elemoid == pe->destvertexid)
458 {
459 List *src_quals = copyObject(pe->dest_quals);
460 List *dest_quals = copyObject(pe->src_quals);
462
463 /* Swap the source and destination varnos in the quals. */
464 ChangeVarNodes((Node *) dest_quals, pe->path_factor->src_pf->factorpos + 1,
465 pe->path_factor->dest_pf->factorpos + 1, 0);
466 ChangeVarNodes((Node *) src_quals, pe->path_factor->dest_pf->factorpos + 1,
467 pe->path_factor->src_pf->factorpos + 1, 0);
468
470 if (edge_qual)
472 else
474 }
475
476 /*
477 * If the given edge element does not connect the adjacent vertex
478 * elements in this path, the path is broken. Abandon this path as
479 * it won't return any rows.
480 */
481 if (edge_qual == NULL)
482 return NULL;
483
485 }
486 else
487 Assert(!pe->src_quals && !pe->dest_quals);
488
489 /*
490 * Create RangeTblEntry for this element table.
491 *
492 * SQL/PGQ standard (Ref. Section 11.19, Access rule 2 and General
493 * rule 4) does not specify whose access privileges to use when
494 * accessing the element tables: property graph owner's or current
495 * user's. It is safer to use current user's privileges so as not to
496 * make property graphs as a hole for unpriviledged data access. This
497 * is inline with the views being security_invoker by default.
498 */
499 rel = table_open(pe->reloid, AccessShareLock);
501 NULL, true, false);
502 table_close(rel, NoLock);
503 path_query->rtable = lappend(path_query->rtable, pni->p_rte);
504 path_query->rteperminfos = lappend(path_query->rteperminfos, pni->p_perminfo);
505 pni->p_rte->perminfoindex = list_length(path_query->rteperminfos);
507 rtr->rtindex = list_length(path_query->rtable);
508 fromlist = lappend(fromlist, rtr);
509
510 /*
511 * Make sure that the assumption mentioned in create_pe_for_element()
512 * holds true; that the elements' RangeTblEntrys are added in the
513 * order in which their respective path factors appear in the list of
514 * path factors representing the path pattern.
515 */
516 Assert(pf->factorpos + 1 == rtr->rtindex);
517
518 if (pf->whereClause)
519 {
520 Node *tr;
521
522 tr = replace_property_refs(rte->relid, pf->whereClause, list_make1(pe));
523
525 }
526 }
527
528 if (rte->graph_pattern->whereClause)
529 {
531 (Node *) rte->graph_pattern->whereClause,
532 graph_path);
533
535 }
536
537 path_query->jointree = makeFromExpr(fromlist,
539
540 /* Construct query targetlist from COLUMNS specification of GRAPH_TABLE. */
541 path_query->targetList = castNode(List,
543 (Node *) rte->graph_table_columns,
544 graph_path));
545
546 /*
547 * Mark the columns being accessed in the path query as requiring SELECT
548 * privilege. Any lateral columns should have been handled when the
549 * corresponding ColumnRefs were transformed. Ignore those here.
550 */
552 foreach_node(Var, var, vars)
553 {
555 rt_fetch(var->varno, path_query->rtable));
556
557 /* Must offset the attnum to fit in a bitmapset */
558 perminfo->selectedCols = bms_add_member(perminfo->selectedCols,
559 var->varattno - FirstLowInvalidHeapAttributeNumber);
560 }
561
562 return path_query;
563}
564
565/*
566 * Construct a query which would not return any rows.
567 *
568 * More details in the prologue of generate_queries_for_path_pattern().
569 */
570static Query *
572{
573 Query *query = makeNode(Query);
574
575 query->commandType = CMD_SELECT;
576 query->rtable = NIL;
577 query->rteperminfos = NIL;
578 query->jointree = makeFromExpr(NIL, (Node *) makeBoolConst(false, false));
579
580 /*
581 * Even though no rows are returned, the result still projects the same
582 * columns as projected by GRAPH_TABLE clause. Do this by constructing a
583 * target list full of NULL values.
584 */
585 foreach_node(TargetEntry, te, rte->graph_table_columns)
586 {
587 Node *nte = (Node *) te->expr;
588
590 query->targetList = lappend(query->targetList, te);
591 }
592
593 return query;
594}
595
596/*
597 * Construct a query which is UNION of given path queries.
598 *
599 * The UNION query derives collations of its targetlist entries from the
600 * corresponding targetlist entries of the path queries. The targetlists of path
601 * queries being UNION'ed already have collations assigned. No separate
602 * collation assignment required in this function.
603 *
604 * The function destroys given pathqueries list while constructing
605 * SetOperationStmt recursively. Hence the function always returns with
606 * `pathqueries` set to NIL.
607 */
608static Query *
610{
611 List *rtable = NIL;
615 int resno;
616 ListCell *lctl,
617 *lct,
618 *lcm,
619 *lcc;
620
622
623 /* If there's only one pathquery, no need to construct a UNION query. */
624 if (list_length(*pathqueries) == 1)
625 {
626 *pathqueries = NIL;
627 return sampleQuery;
628 }
629
632
633 /* Encapsulate the set operation statement into a Query. */
635 union_query->commandType = CMD_SELECT;
636 union_query->rtable = rtable;
637 union_query->setOperations = (Node *) sostmt;
638 union_query->rteperminfos = NIL;
639 union_query->jointree = makeFromExpr(NIL, NULL);
640
641 /*
642 * Generate dummy targetlist for outer query using column names from one
643 * of the queries and common datatypes/collations of topmost set
644 * operation. It shouldn't matter which query. Also it shouldn't matter
645 * which RT index is used as varno in the target list entries, as long as
646 * it corresponds to a real RT entry; else funny things may happen when
647 * the tree is mashed by rule rewriting. So we use 1 since there's always
648 * one RT entry at least.
649 */
650 Assert(rt_fetch(1, rtable));
651 union_query->targetList = NULL;
652 resno = 1;
653 forfour(lct, sostmt->colTypes,
654 lcm, sostmt->colTypmods,
655 lcc, sostmt->colCollations,
656 lctl, sampleQuery->targetList)
657 {
662 char *colName;
664 Var *var;
665
666 Assert(!sample_tle->resjunk);
667 colName = pstrdup(sample_tle->resname);
668 var = makeVar(1, sample_tle->resno, colType, colTypmod, colCollation, 0);
669 var->location = exprLocation((Node *) sample_tle->expr);
670 tle = makeTargetEntry((Expr *) var, (AttrNumber) resno++, colName, false);
671 union_query->targetList = lappend(union_query->targetList, tle);
672 }
673
674 *pathqueries = NIL;
675 return union_query;
676}
677
678/*
679 * Construct a query which is UNION of all the given path queries.
680 *
681 * The function destroys given pathqueries list while constructing
682 * SetOperationStmt recursively.
683 */
684static Node *
686{
688 Query *lquery;
689 Node *rarg;
693
694 /* Recursion termination condition. */
695 if (list_length(pathqueries) == 0)
696 {
697 *targetlist = NIL;
698 return NULL;
699 }
700
702
704 false, false);
705 *rtable = lappend(*rtable, pni->p_rte);
706 lrtr->rtindex = list_length(*rtable);
708 if (rarg == NULL)
709 {
710 /*
711 * No further path queries in the list. Convert the last query into a
712 * RangeTblRef as expected by SetOperationStmt. Extract a list of the
713 * non-junk TLEs for upper-level processing.
714 */
715 if (targetlist)
716 {
717 *targetlist = NIL;
718 foreach_node(TargetEntry, tle, lquery->targetList)
719 {
720 if (!tle->resjunk)
721 *targetlist = lappend(*targetlist, tle);
722 }
723 }
724 return (Node *) lrtr;
725 }
726
728 sostmt->op = SETOP_UNION;
729 sostmt->all = true;
730 sostmt->larg = (Node *) lrtr;
731 sostmt->rarg = rarg;
732 constructSetOpTargetlist(NULL, sostmt, lquery->targetList, rtargetlist, targetlist, "UNION", false);
733
734 return (Node *) sostmt;
735}
736
737/*
738 * Construct a path_element object for the graph element given by `elemoid`
739 * statisfied by the path factor `pf`.
740 *
741 * If the type of graph element does not fit the element pattern kind, the
742 * function returns NULL.
743 */
744static struct path_element *
746{
749 struct path_element *pe;
750
751 if (!eletup)
752 elog(ERROR, "cache lookup failed for property graph element %u", elemoid);
754
755 if ((pgeform->pgekind == PGEKIND_VERTEX && pf->kind != VERTEX_PATTERN) ||
756 (pgeform->pgekind == PGEKIND_EDGE && !IS_EDGE_PATTERN(pf->kind)))
757 {
759 return NULL;
760 }
761
763 pe->path_factor = pf;
764 pe->elemoid = elemoid;
765 pe->reloid = pgeform->pgerelid;
766
767 /*
768 * When a path is converted into a query
769 * (generate_query_for_graph_path()), a RangeTblEntry will be created for
770 * every element in the path. Fixing rtindexes of RangeTblEntrys here
771 * makes it possible to craft elements' qual expressions only once while
772 * we have access to the catalog entry. Otherwise they need to be crafted
773 * as many times as the number of paths a given element appears in,
774 * fetching catalog entry again each time. Hence we simply assume
775 * RangeTblEntrys will be created in the same order in which the
776 * corresponding path factors appear in the list of path factors
777 * representing a path pattern. That way their rtindexes will be same as
778 * path_factor::factorpos + 1.
779 */
780 if (IS_EDGE_PATTERN(pf->kind))
781 {
782 pe->srcvertexid = pgeform->pgesrcvertexid;
783 pe->destvertexid = pgeform->pgedestvertexid;
784 Assert(pf->src_pf && pf->dest_pf);
785
786 pe->src_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->src_pf->factorpos + 1,
787 pe->srcvertexid,
791 pe->dest_quals = build_edge_vertex_link_quals(eletup, pf->factorpos + 1, pf->dest_pf->factorpos + 1,
792 pe->destvertexid,
796 }
797
799
800 return pe;
801}
802
803/*
804 * Returns the list of OIDs of graph labels which the given label expression
805 * resolves to in the given property graph.
806 */
807static List *
808get_labels_for_expr(Oid propgraphid, Node *labelexpr)
809{
811
812 if (!labelexpr)
813 {
814 Relation rel;
815 SysScanDesc scan;
816 ScanKeyData key[1];
818
819 /*
820 * According to section 9.2 "Contextual inference of a set of labels"
821 * subclause 2.a.ii of SQL/PGQ standard, element pattern which does
822 * not have a label expression is considered to have label expression
823 * equivalent to '%|!%' which is set of all labels.
824 */
825 label_oids = NIL;
827 ScanKeyInit(&key[0],
830 F_OIDEQ, ObjectIdGetDatum(propgraphid));
832 true, NULL, 1, key);
833 while (HeapTupleIsValid(tup = systable_getnext(scan)))
834 {
836
838 }
839 systable_endscan(scan);
841 }
842 else if (IsA(labelexpr, GraphLabelRef))
843 {
845
846 label_oids = list_make1_oid(glr->labelid);
847 }
848 else if (IsA(labelexpr, BoolExpr))
849 {
850 BoolExpr *be = castNode(BoolExpr, labelexpr);
851 List *label_exprs = be->args;
852
853 label_oids = NIL;
856 }
857 else
858 {
859 /*
860 * should not reach here since gram.y will not generate a label
861 * expression with other node types.
862 */
863 elog(ERROR, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
864 }
865
866 return label_oids;
867}
868
869/*
870 * Return a list of all the graph elements that satisfy the graph element pattern
871 * represented by the given path_factor `pf`.
872 *
873 * First we find all the graph labels that satisfy the label expression in path
874 * factor. Each label is associated with one or more graph elements. A union of
875 * all such elements satisfies the element pattern. We create one path_element
876 * object representing every element whose graph element kind qualifies the
877 * element pattern kind. A list of all such path_element objects is returned.
878 *
879 * Note that we need to report an error for an explicitly specified label which
880 * is not associated with any graph element of the required kind. So we have to
881 * treat each label separately. Without that requirement we could have collected
882 * all the unique elements first and then created path_element objects for them
883 * to simplify the code.
884 */
885static List *
887{
888 List *label_oids = get_labels_for_expr(propgraphid, pf->labelexpr);
893 Relation rel;
894 SysScanDesc scan;
895 ScanKeyData key[1];
897
898 /*
899 * A property graph element can be either a vertex or an edge. Other types
900 * of path factors like nested path pattern need to be handled separately
901 * when supported.
902 */
903 Assert(pf->kind == VERTEX_PATTERN || IS_EDGE_PATTERN(pf->kind));
904
907 {
908 bool found = false;
909
910 ScanKeyInit(&key[0],
915 NULL, 1, key);
916 while (HeapTupleIsValid(tup = systable_getnext(scan)))
917 {
919 Oid elem_oid = label_elem->pgelelid;
920
922 {
923 /*
924 * Create path_element object if the new element qualifies the
925 * element pattern kind.
926 */
928
929 if (pe)
930 {
932
933 /* Remember qualified elements. */
935 found = true;
936 }
937
938 /*
939 * Rememeber qualified and unqualified elements processed so
940 * far to avoid processing already processed elements again.
941 */
943 }
945 {
946 /*
947 * The graph element is known to qualify the given element
948 * pattern. Flag that the current label has at least one
949 * qualified element associated with it.
950 */
951 found = true;
952 }
953 }
954
955 if (!found)
956 {
957 /*
958 * We did not find any qualified element associated with this
959 * label. The label or its properties can not be associated with
960 * the given element pattern. Throw an error if the label was
961 * explicitly specified in the element pattern. Otherwise remember
962 * it for later use.
963 */
964 if (!pf->labelexpr)
966 else
969 errmsg("no property graph element of type \"%s\" has label \"%s\" associated with it in property graph \"%s\"",
970 pf->kind == VERTEX_PATTERN ? "vertex" : "edge",
972 get_rel_name(propgraphid))));
973 }
974
975 systable_endscan(scan);
976 }
978
979 /*
980 * Remove the labels which were not explicitly mentioned in the label
981 * expression but do not have any qualified elements associated with them.
982 * Properties associated with such labels may not be referenced. See
983 * replace_property_refs_mutator() for more details.
984 */
986
987 return path_elements;
988}
989
990/*
991 * Mutating property references into table variables
992 */
993
999
1000static Node *
1002{
1003 if (node == NULL)
1004 return NULL;
1005 if (IsA(node, Var))
1006 {
1007 Var *var = (Var *) node;
1008 Var *newvar = copyObject(var);
1009
1010 /*
1011 * If it's already a Var, then it was a lateral reference. Since we
1012 * are in a subquery after the rewrite, we have to increase the level
1013 * by one.
1014 */
1015 newvar->varlevelsup++;
1016
1017 return (Node *) newvar;
1018 }
1019 else if (IsA(node, GraphPropertyRef))
1020 {
1022 Node *n = NULL;
1026
1027 foreach_ptr(struct path_element, m, context->mappings)
1028 {
1029 if (m->path_factor->variable && strcmp(gpr->elvarname, m->path_factor->variable) == 0)
1030 {
1031 found_mapping = m;
1032 break;
1033 }
1034 }
1035
1036 /*
1037 * transformGraphTablePropertyRef() would not create a
1038 * GraphPropertyRef for a variable which is not present in the graph
1039 * path pattern.
1040 */
1042
1043 mapping_factor = found_mapping->path_factor;
1044
1045 /*
1046 * Find property definition for given element through any of the
1047 * associated labels qualifying the given element pattern.
1048 */
1050 {
1055
1057 {
1059 ObjectIdGetDatum(gpr->propid));
1060
1061 if (!tup)
1062 {
1063 /*
1064 * The label is associated with the given element but it
1065 * is not associated with the required property. Check
1066 * next label.
1067 */
1068 continue;
1069 }
1070
1073 ChangeVarNodes(n, 1, mapping_factor->factorpos + 1, 0);
1074
1076 }
1077 else
1078 {
1079 /*
1080 * Label is not associated with the element but it may be
1081 * associated with the property through some other element.
1082 * Save it for later use.
1083 */
1085 }
1086 }
1087
1088 /* See if we can resolve the property in some other way. */
1089 if (!n)
1090 {
1091 bool prop_associated = false;
1092
1094 {
1096 {
1097 prop_associated = true;
1098 break;
1099 }
1100 }
1101
1102 if (prop_associated)
1103 {
1104 /*
1105 * The property is associated with at least one of the labels
1106 * that satisfy given element pattern. If it's associated with
1107 * the given element (through some other label), use
1108 * correspondig value expression. Otherwise NULL. Ref. SQL/PGQ
1109 * standard section 6.5 Property Reference, General Rule 2.b.
1110 */
1111 n = get_element_property_expr(found_mapping->elemoid, gpr->propid,
1112 mapping_factor->factorpos + 1);
1113
1114 if (!n)
1115 n = (Node *) makeNullConst(gpr->typeId, gpr->typmod, gpr->collation);
1116 }
1117
1118 }
1119
1120 if (!n)
1121 ereport(ERROR,
1123 errmsg("property \"%s\" for element variable \"%s\" not found",
1124 get_propgraph_property_name(gpr->propid), mapping_factor->variable));
1125
1126 return n;
1127 }
1128
1130}
1131
1132static Node *
1133replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
1134{
1135 struct replace_property_refs_context context;
1136
1137 context.mappings = mappings;
1138 context.propgraphid = propgraphid;
1139
1141}
1142
1143/*
1144 * Build join qualification expressions between edge and vertex tables.
1145 */
1146static List *
1148{
1149 List *quals = NIL;
1151 Datum datum;
1152 Datum *d1,
1153 *d2,
1154 *d3;
1155 int n1,
1156 n2,
1157 n3;
1158 ParseState *pstate = make_parsestate(NULL);
1160
1162
1165
1168
1171
1172 if (n1 != n2)
1173 elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
1174 if (n1 != n3)
1175 elog(ERROR, "array size key (%d) vs operator (%d) mismatch for element ID %u", catalog_key_attnum, catalog_eqop_attnum, pgeform->oid);
1176
1177 for (int i = 0; i < n1; i++)
1178 {
1181 Oid eqop = DatumGetObjectId(d3[i]);
1182 Var *keyvar;
1183 Var *refvar;
1184 Oid atttypid;
1185 int32 atttypmod;
1186 Oid attcoll;
1187 HeapTuple tup;
1189 List *args;
1193
1194 get_atttypetypmodcoll(pgeform->pgerelid, keyattn, &atttypid, &atttypmod, &attcoll);
1195 keyvar = makeVar(edgerti, keyattn, atttypid, atttypmod, attcoll, 0);
1196 get_atttypetypmodcoll(refrelid, refattn, &atttypid, &atttypmod, &attcoll);
1197 refvar = makeVar(refrti, refattn, atttypid, atttypmod, attcoll, 0);
1198
1200 if (!HeapTupleIsValid(tup))
1201 elog(ERROR, "cache lookup failed for operator %u", eqop);
1203 /* An equality operator is a binary operator returning boolean result. */
1204 Assert(opform->oprkind == 'b'
1205 && RegProcedureIsValid(opform->oprcode)
1206 && opform->oprresult == BOOLOID
1207 && !get_func_retset(opform->oprcode));
1208
1209 /*
1210 * Prepare operands and cast them to the types required by the
1211 * equality operator. Similar to PK/FK quals, referenced vertex key is
1212 * used as left operand and referencing edge key is used as right
1213 * operand.
1214 */
1215 args = list_make2(refvar, keyvar);
1218 declared_arg_types[0] = opform->oprleft;
1219 declared_arg_types[1] = opform->oprright;
1221
1223 linkqual->opno = opform->oid;
1224 linkqual->opfuncid = opform->oprcode;
1225 linkqual->opresulttype = opform->oprresult;
1226 linkqual->opretset = false;
1227 /* opcollid and inputcollid will be set by parse_collate.c */
1228 linkqual->args = args;
1229 linkqual->location = -1;
1230
1232 quals = lappend(quals, linkqual);
1233 }
1234
1235 assign_expr_collations(pstate, (Node *) quals);
1236
1237 return quals;
1238}
1239
1240/*
1241 * Check if the given property is associated with the given label.
1242 *
1243 * A label projects the same set of properties through every element it is
1244 * associated with. Find any of the elements and return true if that element is
1245 * associated with the given property. False otherwise.
1246 */
1247static bool
1276
1277/*
1278 * If given element has the given property associated with it, through any of
1279 * the associated labels, return value expression of the property. Otherwise
1280 * NULL.
1281 */
1282static 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:861
#define Assert(condition)
Definition c.h:942
int32_t int32
Definition c.h:611
#define OidIsValid(objectId)
Definition c.h:857
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
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:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:514
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:3959
bool get_func_retset(Oid funcid)
Definition lsyscache.c:1962
char * get_propgraph_label_name(Oid labeloid)
Definition lsyscache.c:3941
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:2272
#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:242
#define list_make1(x1)
Definition pg_list.h:212
#define foreach_ptr(type, var, lst)
Definition pg_list.h:469
static void * list_nth(const List *list, int n)
Definition pg_list.h:299
#define linitial(l)
Definition pg_list.h:178
#define foreach_node(type, var, lst)
Definition pg_list.h:496
#define forfour(cell1, list1, cell2, list2, cell3, list3, cell4, list4)
Definition pg_list.h:575
#define foreach_oid(var, lst)
Definition pg_list.h:471
#define list_nth_node(type, list, n)
Definition pg_list.h:327
#define lfirst_oid(lc)
Definition pg_list.h:174
#define list_make2(x1, x2)
Definition pg_list.h:214
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:182
List * rtable
Definition parsenodes.h:175
CmdType commandType
Definition parsenodes.h:121
List * targetList
Definition parsenodes.h:198
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:264
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:230
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:625
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220
#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