Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * propgraphcmds.c
4 : * property graph manipulation
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * src/backend/commands/propgraphcmds.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
15 : #include "access/genam.h"
16 : #include "access/htup_details.h"
17 : #include "access/nbtree.h"
18 : #include "access/table.h"
19 : #include "access/xact.h"
20 : #include "catalog/catalog.h"
21 : #include "catalog/indexing.h"
22 : #include "catalog/namespace.h"
23 : #include "catalog/pg_class.h"
24 : #include "catalog/pg_collation_d.h"
25 : #include "catalog/pg_operator_d.h"
26 : #include "catalog/pg_propgraph_element.h"
27 : #include "catalog/pg_propgraph_element_label.h"
28 : #include "catalog/pg_propgraph_label.h"
29 : #include "catalog/pg_propgraph_label_property.h"
30 : #include "catalog/pg_propgraph_property.h"
31 : #include "commands/defrem.h"
32 : #include "commands/propgraphcmds.h"
33 : #include "commands/tablecmds.h"
34 : #include "nodes/nodeFuncs.h"
35 : #include "parser/parse_coerce.h"
36 : #include "parser/parse_collate.h"
37 : #include "parser/parse_oper.h"
38 : #include "parser/parse_relation.h"
39 : #include "parser/parse_target.h"
40 : #include "utils/array.h"
41 : #include "utils/builtins.h"
42 : #include "utils/fmgroids.h"
43 : #include "utils/inval.h"
44 : #include "utils/lsyscache.h"
45 : #include "utils/rel.h"
46 : #include "utils/ruleutils.h"
47 : #include "utils/syscache.h"
48 :
49 :
50 : struct element_info
51 : {
52 : Oid elementid;
53 : char kind;
54 : Oid relid;
55 : char *aliasname;
56 : ArrayType *key;
57 :
58 : char *srcvertex;
59 : Oid srcvertexid;
60 : Oid srcrelid;
61 : ArrayType *srckey;
62 : ArrayType *srcref;
63 : ArrayType *srceqop;
64 :
65 : char *destvertex;
66 : Oid destvertexid;
67 : Oid destrelid;
68 : ArrayType *destkey;
69 : ArrayType *destref;
70 : ArrayType *desteqop;
71 :
72 : List *labels;
73 : };
74 :
75 :
76 : static ArrayType *propgraph_element_get_key(ParseState *pstate, const List *keycols, Relation element_rel,
77 : const char *aliasname, int location);
78 : static void propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols,
79 : Relation edge_rel, Relation ref_rel,
80 : const char *aliasname, int location, const char *type,
81 : ArrayType **outkey, ArrayType **outref, ArrayType **outeqop);
82 : static AttrNumber *array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel);
83 : static ArrayType *array_from_attnums(int numattrs, const AttrNumber *attnums);
84 : static Oid insert_element_record(ObjectAddress pgaddress, struct element_info *einfo);
85 : static Oid insert_label_record(Oid graphid, Oid peoid, const char *label);
86 : static void insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties);
87 : static void insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr);
88 : static void check_element_properties(Oid peoid);
89 : static void check_element_label_properties(Oid ellabeloid);
90 : static void check_all_labels_properties(Oid pgrelid);
91 : static Oid get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location);
92 : static Oid get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location);
93 : static Oid get_element_relid(Oid peid);
94 : static List *get_graph_label_ids(Oid graphid);
95 : static List *get_label_element_label_ids(Oid labelid);
96 : static List *get_element_label_property_names(Oid ellabeloid);
97 : static List *get_graph_property_ids(Oid graphid);
98 :
99 :
100 : /*
101 : * CREATE PROPERTY GRAPH
102 : */
103 : ObjectAddress
104 201 : CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt)
105 : {
106 201 : CreateStmt *cstmt = makeNode(CreateStmt);
107 : char components_persistence;
108 : ListCell *lc;
109 : ObjectAddress pgaddress;
110 201 : List *vertex_infos = NIL;
111 201 : List *edge_infos = NIL;
112 201 : List *element_aliases = NIL;
113 201 : List *element_oids = NIL;
114 :
115 201 : if (stmt->pgname->relpersistence == RELPERSISTENCE_UNLOGGED)
116 4 : ereport(ERROR,
117 : (errcode(ERRCODE_SYNTAX_ERROR),
118 : errmsg("property graphs cannot be unlogged because they do not have storage")));
119 :
120 197 : components_persistence = RELPERSISTENCE_PERMANENT;
121 :
122 539 : foreach(lc, stmt->vertex_tables)
123 : {
124 350 : PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
125 : struct element_info *vinfo;
126 : Relation rel;
127 :
128 350 : vinfo = palloc0_object(struct element_info);
129 350 : vinfo->kind = PGEKIND_VERTEX;
130 :
131 350 : vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
132 :
133 346 : rel = table_open(vinfo->relid, NoLock);
134 :
135 346 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
136 8 : components_persistence = RELPERSISTENCE_TEMP;
137 :
138 346 : if (vertex->vtable->alias)
139 8 : vinfo->aliasname = vertex->vtable->alias->aliasname;
140 : else
141 338 : vinfo->aliasname = vertex->vtable->relname;
142 :
143 346 : if (list_member(element_aliases, makeString(vinfo->aliasname)))
144 4 : ereport(ERROR,
145 : (errcode(ERRCODE_DUPLICATE_TABLE),
146 : errmsg("alias \"%s\" used more than once as element table", vinfo->aliasname),
147 : parser_errposition(pstate, vertex->location)));
148 :
149 342 : vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location);
150 :
151 342 : vinfo->labels = vertex->labels;
152 :
153 342 : table_close(rel, NoLock);
154 :
155 342 : vertex_infos = lappend(vertex_infos, vinfo);
156 :
157 342 : element_aliases = lappend(element_aliases, makeString(vinfo->aliasname));
158 : }
159 :
160 346 : foreach(lc, stmt->edge_tables)
161 : {
162 177 : PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc);
163 : struct element_info *einfo;
164 : Relation rel;
165 : ListCell *lc2;
166 : Oid srcrelid;
167 : Oid destrelid;
168 : Relation srcrel;
169 : Relation destrel;
170 :
171 177 : einfo = palloc0_object(struct element_info);
172 177 : einfo->kind = PGEKIND_EDGE;
173 :
174 177 : einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
175 :
176 177 : rel = table_open(einfo->relid, NoLock);
177 :
178 177 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
179 0 : components_persistence = RELPERSISTENCE_TEMP;
180 :
181 177 : if (edge->etable->alias)
182 0 : einfo->aliasname = edge->etable->alias->aliasname;
183 : else
184 177 : einfo->aliasname = edge->etable->relname;
185 :
186 177 : if (list_member(element_aliases, makeString(einfo->aliasname)))
187 0 : ereport(ERROR,
188 : (errcode(ERRCODE_DUPLICATE_TABLE),
189 : errmsg("alias \"%s\" used more than once as element table", einfo->aliasname),
190 : parser_errposition(pstate, edge->location)));
191 :
192 177 : einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location);
193 :
194 177 : einfo->srcvertex = edge->esrcvertex;
195 177 : einfo->destvertex = edge->edestvertex;
196 :
197 177 : srcrelid = 0;
198 177 : destrelid = 0;
199 423 : foreach(lc2, vertex_infos)
200 : {
201 415 : struct element_info *vinfo = lfirst(lc2);
202 :
203 415 : if (strcmp(vinfo->aliasname, edge->esrcvertex) == 0)
204 173 : srcrelid = vinfo->relid;
205 :
206 415 : if (strcmp(vinfo->aliasname, edge->edestvertex) == 0)
207 173 : destrelid = vinfo->relid;
208 :
209 415 : if (srcrelid && destrelid)
210 169 : break;
211 : }
212 177 : if (!srcrelid)
213 4 : ereport(ERROR,
214 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
215 : errmsg("source vertex \"%s\" of edge \"%s\" does not exist",
216 : edge->esrcvertex, einfo->aliasname),
217 : parser_errposition(pstate, edge->location)));
218 173 : if (!destrelid)
219 4 : ereport(ERROR,
220 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
221 : errmsg("destination vertex \"%s\" of edge \"%s\" does not exist",
222 : edge->edestvertex, einfo->aliasname),
223 : parser_errposition(pstate, edge->location)));
224 :
225 169 : srcrel = table_open(srcrelid, NoLock);
226 169 : destrel = table_open(destrelid, NoLock);
227 :
228 169 : propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel,
229 169 : einfo->aliasname, edge->location, "SOURCE",
230 : &einfo->srckey, &einfo->srcref, &einfo->srceqop);
231 161 : propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel,
232 161 : einfo->aliasname, edge->location, "DESTINATION",
233 : &einfo->destkey, &einfo->destref, &einfo->desteqop);
234 :
235 157 : einfo->labels = edge->labels;
236 :
237 157 : table_close(destrel, NoLock);
238 157 : table_close(srcrel, NoLock);
239 :
240 157 : table_close(rel, NoLock);
241 :
242 157 : edge_infos = lappend(edge_infos, einfo);
243 :
244 157 : element_aliases = lappend(element_aliases, makeString(einfo->aliasname));
245 : }
246 :
247 169 : cstmt->relation = stmt->pgname;
248 169 : cstmt->oncommit = ONCOMMIT_NOOP;
249 :
250 : /*
251 : * Automatically make it temporary if any component tables are temporary
252 : * (see also DefineView()).
253 : */
254 169 : if (stmt->pgname->relpersistence == RELPERSISTENCE_PERMANENT
255 157 : && components_persistence == RELPERSISTENCE_TEMP)
256 : {
257 4 : cstmt->relation = copyObject(cstmt->relation);
258 4 : cstmt->relation->relpersistence = RELPERSISTENCE_TEMP;
259 4 : ereport(NOTICE,
260 : (errmsg("property graph \"%s\" will be temporary",
261 : stmt->pgname->relname)));
262 : }
263 :
264 169 : pgaddress = DefineRelation(cstmt, RELKIND_PROPGRAPH, InvalidOid, NULL, NULL);
265 :
266 443 : foreach(lc, vertex_infos)
267 : {
268 294 : struct element_info *vinfo = lfirst(lc);
269 : Oid peoid;
270 :
271 294 : peoid = insert_element_record(pgaddress, vinfo);
272 278 : element_oids = lappend_oid(element_oids, peoid);
273 : }
274 :
275 302 : foreach(lc, edge_infos)
276 : {
277 157 : struct element_info *einfo = lfirst(lc);
278 : Oid peoid;
279 : ListCell *lc2;
280 :
281 : /*
282 : * Look up the vertices again. Now the vertices have OIDs assigned,
283 : * which we need.
284 : */
285 375 : foreach(lc2, vertex_infos)
286 : {
287 375 : struct element_info *vinfo = lfirst(lc2);
288 :
289 375 : if (strcmp(vinfo->aliasname, einfo->srcvertex) == 0)
290 : {
291 157 : einfo->srcvertexid = vinfo->elementid;
292 157 : einfo->srcrelid = vinfo->relid;
293 : }
294 375 : if (strcmp(vinfo->aliasname, einfo->destvertex) == 0)
295 : {
296 157 : einfo->destvertexid = vinfo->elementid;
297 157 : einfo->destrelid = vinfo->relid;
298 : }
299 375 : if (einfo->srcvertexid && einfo->destvertexid)
300 157 : break;
301 : }
302 : Assert(einfo->srcvertexid);
303 : Assert(einfo->destvertexid);
304 : Assert(einfo->srcrelid);
305 : Assert(einfo->destrelid);
306 157 : peoid = insert_element_record(pgaddress, einfo);
307 153 : element_oids = lappend_oid(element_oids, peoid);
308 : }
309 :
310 145 : CommandCounterIncrement();
311 :
312 681 : foreach_oid(peoid, element_oids)
313 399 : check_element_properties(peoid);
314 141 : check_all_labels_properties(pgaddress.objectId);
315 :
316 129 : return pgaddress;
317 : }
318 :
319 : /*
320 : * Process the key clause specified for an element. If key_clause is non-NIL,
321 : * then it is a list of column names. Otherwise, the primary key of the
322 : * relation is used. The return value is an array of column numbers.
323 : */
324 : static ArrayType *
325 583 : propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, const char *aliasname, int location)
326 : {
327 : ArrayType *a;
328 :
329 583 : if (key_clause == NIL)
330 : {
331 116 : Oid pkidx = RelationGetPrimaryKeyIndex(element_rel, false);
332 :
333 116 : if (!pkidx)
334 0 : ereport(ERROR,
335 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
336 : errmsg("no key specified and no suitable primary key exists for definition of element \"%s\"", aliasname),
337 : parser_errposition(pstate, location));
338 : else
339 : {
340 : Relation indexDesc;
341 :
342 116 : indexDesc = index_open(pkidx, AccessShareLock);
343 116 : a = array_from_attnums(indexDesc->rd_index->indkey.dim1, indexDesc->rd_index->indkey.values);
344 116 : index_close(indexDesc, NoLock);
345 : }
346 : }
347 : else
348 : {
349 467 : a = array_from_attnums(list_length(key_clause),
350 467 : array_from_column_list(pstate, key_clause, location, element_rel));
351 : }
352 :
353 583 : return a;
354 : }
355 :
356 : /*
357 : * Process the source or destination link of an edge.
358 : *
359 : * keycols and refcols are column names representing the local and referenced
360 : * (vertex) columns. If they are both NIL, a matching foreign key is looked
361 : * up.
362 : *
363 : * edge_rel and ref_rel are the local and referenced element tables.
364 : *
365 : * aliasname, location, and type are for error messages. type is either
366 : * "SOURCE" or "DESTINATION".
367 : *
368 : * The outputs are arrays of column numbers in outkey and outref.
369 : */
370 : static void
371 402 : propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols,
372 : Relation edge_rel, Relation ref_rel,
373 : const char *aliasname, int location, const char *type,
374 : ArrayType **outkey, ArrayType **outref, ArrayType **outeqop)
375 : {
376 : int nkeys;
377 : AttrNumber *keyattnums;
378 : AttrNumber *refattnums;
379 : Oid *keyeqops;
380 : Datum *datums;
381 :
382 : Assert((keycols && refcols) || (!keycols && !refcols));
383 :
384 402 : if (keycols)
385 : {
386 390 : if (list_length(keycols) != list_length(refcols))
387 0 : ereport(ERROR,
388 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
389 : errmsg("mismatching number of columns in %s vertex definition of edge \"%s\"", type, aliasname),
390 : parser_errposition(pstate, location));
391 :
392 390 : nkeys = list_length(keycols);
393 390 : keyattnums = array_from_column_list(pstate, keycols, location, edge_rel);
394 390 : refattnums = array_from_column_list(pstate, refcols, location, ref_rel);
395 390 : keyeqops = palloc_array(Oid, nkeys);
396 :
397 820 : for (int i = 0; i < nkeys; i++)
398 : {
399 : Oid keytype;
400 : int32 keytypmod;
401 : Oid keycoll;
402 : Oid reftype;
403 : int32 reftypmod;
404 : Oid refcoll;
405 : Oid opc;
406 : Oid opf;
407 : StrategyNumber strategy;
408 :
409 : /*
410 : * Lookup equality operator to be used for edge and vertex key.
411 : * Vertex key is equivalent to primary key and edge key is similar
412 : * to foreign key since edge key references vertex key. Hence
413 : * vertex key is used as left operand and edge key is used as
414 : * right operand. The method used to find the equality operators
415 : * is similar to the method used to find equality operators for
416 : * FK/PK comparison in ATAddForeignKeyConstraint() except that
417 : * opclass of the the vertex key type is used as a starting point.
418 : * Since we need only equality operators we use both BT and HASH
419 : * strategies.
420 : *
421 : * If the required operators do not exist, we can not construct
422 : * quals linking an edge to its adjacent vertexes.
423 : */
424 438 : get_atttypetypmodcoll(RelationGetRelid(edge_rel), keyattnums[i], &keytype, &keytypmod, &keycoll);
425 438 : get_atttypetypmodcoll(RelationGetRelid(ref_rel), refattnums[i], &reftype, &reftypmod, &refcoll);
426 438 : keyeqops[i] = InvalidOid;
427 438 : strategy = BTEqualStrategyNumber;
428 438 : opc = GetDefaultOpClass(reftype, BTREE_AM_OID);
429 438 : if (!OidIsValid(opc))
430 : {
431 0 : opc = GetDefaultOpClass(reftype, HASH_AM_OID);
432 0 : strategy = HTEqualStrategyNumber;
433 : }
434 438 : if (OidIsValid(opc))
435 : {
436 438 : opf = get_opclass_family(opc);
437 438 : if (OidIsValid(opf))
438 : {
439 438 : keyeqops[i] = get_opfamily_member(opf, reftype, keytype, strategy);
440 438 : if (!OidIsValid(keyeqops[i]))
441 : {
442 : /* Last resort, implicit cast. */
443 4 : if (can_coerce_type(1, &keytype, &reftype, COERCION_IMPLICIT))
444 0 : keyeqops[i] = get_opfamily_member(opf, reftype, reftype, strategy);
445 : }
446 : }
447 : }
448 :
449 438 : if (!OidIsValid(keyeqops[i]))
450 4 : ereport(ERROR,
451 : errcode(ERRCODE_SYNTAX_ERROR),
452 : errmsg("no equality operator exists for %s key comparison of edge \"%s\"",
453 : type, aliasname),
454 : parser_errposition(pstate, location));
455 :
456 : /*
457 : * If collations of key attribute and referenced attribute are
458 : * different, an edge may end up being adjacent to undesired
459 : * vertexes. Prohibit such a case.
460 : *
461 : * PK/FK allows different collations as long as they are
462 : * deterministic for backward compatibility. But we can be a bit
463 : * stricter here and follow SQL standard.
464 : */
465 434 : if (keycoll != refcoll &&
466 4 : keycoll != DEFAULT_COLLATION_OID && refcoll != DEFAULT_COLLATION_OID &&
467 4 : OidIsValid(keycoll) && OidIsValid(refcoll))
468 4 : ereport(ERROR,
469 : errcode(ERRCODE_SYNTAX_ERROR),
470 : errmsg("collation mismatch in %s key of edge \"%s\": %s vs. %s",
471 : type, aliasname,
472 : get_collation_name(keycoll), get_collation_name(refcoll)),
473 : parser_errposition(pstate, location));
474 : }
475 : }
476 : else
477 : {
478 12 : ForeignKeyCacheInfo *fk = NULL;
479 :
480 40 : foreach_node(ForeignKeyCacheInfo, tmp, RelationGetFKeyList(edge_rel))
481 : {
482 16 : if (tmp->confrelid == RelationGetRelid(ref_rel))
483 : {
484 8 : if (fk)
485 0 : ereport(ERROR,
486 : errcode(ERRCODE_SYNTAX_ERROR),
487 : errmsg("more than one suitable foreign key exists for %s key of edge \"%s\"", type, aliasname),
488 : parser_errposition(pstate, location));
489 8 : fk = tmp;
490 : }
491 : }
492 :
493 12 : if (!fk)
494 4 : ereport(ERROR,
495 : errcode(ERRCODE_SYNTAX_ERROR),
496 : errmsg("no %s key specified and no suitable foreign key exists for definition of edge \"%s\"", type, aliasname),
497 : parser_errposition(pstate, location));
498 :
499 8 : nkeys = fk->nkeys;
500 8 : keyattnums = fk->conkey;
501 8 : refattnums = fk->confkey;
502 8 : keyeqops = fk->conpfeqop;
503 : }
504 :
505 390 : *outkey = array_from_attnums(nkeys, keyattnums);
506 390 : *outref = array_from_attnums(nkeys, refattnums);
507 390 : datums = palloc_array(Datum, nkeys);
508 828 : for (int i = 0; i < nkeys; i++)
509 438 : datums[i] = ObjectIdGetDatum(keyeqops[i]);
510 390 : *outeqop = construct_array_builtin(datums, nkeys, OIDOID);
511 390 : }
512 :
513 : /*
514 : * Convert list of column names in the specified relation into an array of
515 : * column numbers.
516 : */
517 : static AttrNumber *
518 1247 : array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel)
519 : {
520 : int numattrs;
521 : AttrNumber *attnums;
522 : int i;
523 : ListCell *lc;
524 :
525 1247 : numattrs = list_length(colnames);
526 1247 : attnums = palloc_array(AttrNumber, numattrs);
527 :
528 1247 : i = 0;
529 2751 : foreach(lc, colnames)
530 : {
531 1504 : char *colname = strVal(lfirst(lc));
532 1504 : Oid relid = RelationGetRelid(element_rel);
533 : AttrNumber attnum;
534 :
535 1504 : attnum = get_attnum(relid, colname);
536 1504 : if (!attnum)
537 0 : ereport(ERROR,
538 : (errcode(ERRCODE_UNDEFINED_COLUMN),
539 : errmsg("column \"%s\" of relation \"%s\" does not exist",
540 : colname, get_rel_name(relid)),
541 : parser_errposition(pstate, location)));
542 1504 : attnums[i++] = attnum;
543 : }
544 :
545 2751 : for (int j = 0; j < numattrs; j++)
546 : {
547 1795 : for (int k = j + 1; k < numattrs; k++)
548 : {
549 291 : if (attnums[j] == attnums[k])
550 0 : ereport(ERROR,
551 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
552 : errmsg("graph key columns list must not contain duplicates"),
553 : parser_errposition(pstate, location)));
554 : }
555 : }
556 :
557 1247 : return attnums;
558 : }
559 :
560 : static ArrayType *
561 1363 : array_from_attnums(int numattrs, const AttrNumber *attnums)
562 : {
563 : Datum *attnumsd;
564 :
565 1363 : attnumsd = palloc_array(Datum, numattrs);
566 :
567 3015 : for (int i = 0; i < numattrs; i++)
568 1652 : attnumsd[i] = Int16GetDatum(attnums[i]);
569 :
570 1363 : return construct_array_builtin(attnumsd, numattrs, INT2OID);
571 : }
572 :
573 : static void
574 1283 : array_of_attnums_to_objectaddrs(Oid relid, ArrayType *arr, ObjectAddresses *addrs)
575 : {
576 : Datum *attnumsd;
577 : int numattrs;
578 :
579 1283 : deconstruct_array_builtin(arr, INT2OID, &attnumsd, NULL, &numattrs);
580 :
581 2835 : for (int i = 0; i < numattrs; i++)
582 : {
583 : ObjectAddress referenced;
584 :
585 1552 : ObjectAddressSubSet(referenced, RelationRelationId, relid, DatumGetInt16(attnumsd[i]));
586 1552 : add_exact_object_address(&referenced, addrs);
587 : }
588 1283 : }
589 :
590 : static void
591 386 : array_of_opers_to_objectaddrs(ArrayType *arr, ObjectAddresses *addrs)
592 : {
593 : Datum *opersd;
594 : int numopers;
595 :
596 386 : deconstruct_array_builtin(arr, OIDOID, &opersd, NULL, &numopers);
597 :
598 820 : for (int i = 0; i < numopers; i++)
599 : {
600 : ObjectAddress referenced;
601 :
602 434 : ObjectAddressSet(referenced, OperatorRelationId, DatumGetObjectId(opersd[i]));
603 434 : add_exact_object_address(&referenced, addrs);
604 : }
605 386 : }
606 :
607 : /*
608 : * Insert a record for an element into the pg_propgraph_element catalog. Also
609 : * inserts labels and properties into their respective catalogs.
610 : */
611 : static Oid
612 511 : insert_element_record(ObjectAddress pgaddress, struct element_info *einfo)
613 : {
614 511 : Oid graphid = pgaddress.objectId;
615 : Relation rel;
616 : NameData aliasname;
617 : Oid peoid;
618 511 : Datum values[Natts_pg_propgraph_element] = {0};
619 511 : bool nulls[Natts_pg_propgraph_element] = {0};
620 : HeapTuple tup;
621 : ObjectAddress myself;
622 : ObjectAddress referenced;
623 : ObjectAddresses *addrs;
624 :
625 511 : rel = table_open(PropgraphElementRelationId, RowExclusiveLock);
626 :
627 511 : peoid = GetNewOidWithIndex(rel, PropgraphElementObjectIndexId, Anum_pg_propgraph_element_oid);
628 511 : einfo->elementid = peoid;
629 511 : values[Anum_pg_propgraph_element_oid - 1] = ObjectIdGetDatum(peoid);
630 511 : values[Anum_pg_propgraph_element_pgepgid - 1] = ObjectIdGetDatum(graphid);
631 511 : values[Anum_pg_propgraph_element_pgerelid - 1] = ObjectIdGetDatum(einfo->relid);
632 511 : namestrcpy(&aliasname, einfo->aliasname);
633 511 : values[Anum_pg_propgraph_element_pgealias - 1] = NameGetDatum(&aliasname);
634 511 : values[Anum_pg_propgraph_element_pgekind - 1] = CharGetDatum(einfo->kind);
635 511 : values[Anum_pg_propgraph_element_pgesrcvertexid - 1] = ObjectIdGetDatum(einfo->srcvertexid);
636 511 : values[Anum_pg_propgraph_element_pgedestvertexid - 1] = ObjectIdGetDatum(einfo->destvertexid);
637 511 : values[Anum_pg_propgraph_element_pgekey - 1] = PointerGetDatum(einfo->key);
638 :
639 511 : if (einfo->srckey)
640 193 : values[Anum_pg_propgraph_element_pgesrckey - 1] = PointerGetDatum(einfo->srckey);
641 : else
642 318 : nulls[Anum_pg_propgraph_element_pgesrckey - 1] = true;
643 511 : if (einfo->srcref)
644 193 : values[Anum_pg_propgraph_element_pgesrcref - 1] = PointerGetDatum(einfo->srcref);
645 : else
646 318 : nulls[Anum_pg_propgraph_element_pgesrcref - 1] = true;
647 511 : if (einfo->srceqop)
648 193 : values[Anum_pg_propgraph_element_pgesrceqop - 1] = PointerGetDatum(einfo->srceqop);
649 : else
650 318 : nulls[Anum_pg_propgraph_element_pgesrceqop - 1] = true;
651 511 : if (einfo->destkey)
652 193 : values[Anum_pg_propgraph_element_pgedestkey - 1] = PointerGetDatum(einfo->destkey);
653 : else
654 318 : nulls[Anum_pg_propgraph_element_pgedestkey - 1] = true;
655 511 : if (einfo->destref)
656 193 : values[Anum_pg_propgraph_element_pgedestref - 1] = PointerGetDatum(einfo->destref);
657 : else
658 318 : nulls[Anum_pg_propgraph_element_pgedestref - 1] = true;
659 511 : if (einfo->desteqop)
660 193 : values[Anum_pg_propgraph_element_pgedesteqop - 1] = PointerGetDatum(einfo->desteqop);
661 : else
662 318 : nulls[Anum_pg_propgraph_element_pgedesteqop - 1] = true;
663 :
664 511 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
665 511 : CatalogTupleInsert(rel, tup);
666 511 : heap_freetuple(tup);
667 :
668 511 : ObjectAddressSet(myself, PropgraphElementRelationId, peoid);
669 :
670 : /* Add dependency on the property graph */
671 511 : recordDependencyOn(&myself, &pgaddress, DEPENDENCY_AUTO);
672 :
673 511 : addrs = new_object_addresses();
674 :
675 : /* Add dependency on the relation */
676 511 : ObjectAddressSet(referenced, RelationRelationId, einfo->relid);
677 511 : add_exact_object_address(&referenced, addrs);
678 511 : array_of_attnums_to_objectaddrs(einfo->relid, einfo->key, addrs);
679 :
680 : /*
681 : * Add dependencies on vertices and equality operators used for key
682 : * comparison.
683 : */
684 511 : if (einfo->srcvertexid)
685 : {
686 193 : ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->srcvertexid);
687 193 : add_exact_object_address(&referenced, addrs);
688 193 : array_of_attnums_to_objectaddrs(einfo->relid, einfo->srckey, addrs);
689 193 : array_of_attnums_to_objectaddrs(einfo->srcrelid, einfo->srcref, addrs);
690 193 : array_of_opers_to_objectaddrs(einfo->srceqop, addrs);
691 : }
692 511 : if (einfo->destvertexid)
693 : {
694 193 : ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->destvertexid);
695 193 : add_exact_object_address(&referenced, addrs);
696 193 : array_of_attnums_to_objectaddrs(einfo->relid, einfo->destkey, addrs);
697 193 : array_of_attnums_to_objectaddrs(einfo->destrelid, einfo->destref, addrs);
698 193 : array_of_opers_to_objectaddrs(einfo->desteqop, addrs);
699 : }
700 :
701 511 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
702 :
703 511 : table_close(rel, NoLock);
704 :
705 511 : if (einfo->labels)
706 : {
707 : ListCell *lc;
708 :
709 1110 : foreach(lc, einfo->labels)
710 : {
711 627 : PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
712 : Oid ellabeloid;
713 :
714 627 : if (lp->label)
715 256 : ellabeloid = insert_label_record(graphid, peoid, lp->label);
716 : else
717 371 : ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname);
718 627 : insert_property_records(graphid, ellabeloid, einfo->relid, lp->properties);
719 :
720 599 : CommandCounterIncrement();
721 : }
722 : }
723 : else
724 : {
725 : Oid ellabeloid;
726 0 : PropGraphProperties *pr = makeNode(PropGraphProperties);
727 :
728 0 : pr->all = true;
729 0 : pr->location = -1;
730 :
731 0 : ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname);
732 0 : insert_property_records(graphid, ellabeloid, einfo->relid, pr);
733 : }
734 :
735 483 : return peoid;
736 : }
737 :
738 : /*
739 : * Insert records for a label into the pg_propgraph_label and
740 : * pg_propgraph_element_label catalogs, and register dependencies.
741 : *
742 : * Returns the OID of the new pg_propgraph_element_label record.
743 : */
744 : static Oid
745 655 : insert_label_record(Oid graphid, Oid peoid, const char *label)
746 : {
747 : Oid labeloid;
748 : Oid ellabeloid;
749 :
750 : /*
751 : * Insert into pg_propgraph_label if not already existing.
752 : */
753 655 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(graphid), CStringGetDatum(label));
754 655 : if (!labeloid)
755 : {
756 : Relation rel;
757 506 : Datum values[Natts_pg_propgraph_label] = {0};
758 506 : bool nulls[Natts_pg_propgraph_label] = {0};
759 : NameData labelname;
760 : HeapTuple tup;
761 : ObjectAddress myself;
762 : ObjectAddress referenced;
763 :
764 506 : rel = table_open(PropgraphLabelRelationId, RowExclusiveLock);
765 :
766 506 : labeloid = GetNewOidWithIndex(rel, PropgraphLabelObjectIndexId, Anum_pg_propgraph_label_oid);
767 506 : values[Anum_pg_propgraph_label_oid - 1] = ObjectIdGetDatum(labeloid);
768 506 : values[Anum_pg_propgraph_label_pglpgid - 1] = ObjectIdGetDatum(graphid);
769 506 : namestrcpy(&labelname, label);
770 506 : values[Anum_pg_propgraph_label_pgllabel - 1] = NameGetDatum(&labelname);
771 :
772 506 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
773 506 : CatalogTupleInsert(rel, tup);
774 506 : heap_freetuple(tup);
775 :
776 506 : ObjectAddressSet(myself, PropgraphLabelRelationId, labeloid);
777 :
778 506 : ObjectAddressSet(referenced, RelationRelationId, graphid);
779 506 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
780 :
781 506 : table_close(rel, NoLock);
782 : }
783 :
784 : /*
785 : * Insert into pg_propgraph_element_label
786 : */
787 : {
788 : Relation rel;
789 655 : Datum values[Natts_pg_propgraph_element_label] = {0};
790 655 : bool nulls[Natts_pg_propgraph_element_label] = {0};
791 : HeapTuple tup;
792 : ObjectAddress myself;
793 : ObjectAddress referenced;
794 :
795 655 : rel = table_open(PropgraphElementLabelRelationId, RowExclusiveLock);
796 :
797 655 : ellabeloid = GetNewOidWithIndex(rel, PropgraphElementLabelObjectIndexId, Anum_pg_propgraph_element_label_oid);
798 655 : values[Anum_pg_propgraph_element_label_oid - 1] = ObjectIdGetDatum(ellabeloid);
799 655 : values[Anum_pg_propgraph_element_label_pgellabelid - 1] = ObjectIdGetDatum(labeloid);
800 655 : values[Anum_pg_propgraph_element_label_pgelelid - 1] = ObjectIdGetDatum(peoid);
801 :
802 655 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
803 655 : CatalogTupleInsert(rel, tup);
804 655 : heap_freetuple(tup);
805 :
806 655 : ObjectAddressSet(myself, PropgraphElementLabelRelationId, ellabeloid);
807 :
808 655 : ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid);
809 655 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
810 655 : ObjectAddressSet(referenced, PropgraphElementRelationId, peoid);
811 655 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
812 :
813 655 : table_close(rel, NoLock);
814 : }
815 :
816 655 : return ellabeloid;
817 : }
818 :
819 : /*
820 : * Insert records for properties into the pg_propgraph_property catalog.
821 : */
822 : static void
823 663 : insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties)
824 : {
825 663 : List *proplist = NIL;
826 : ParseState *pstate;
827 : ParseNamespaceItem *nsitem;
828 : List *tp;
829 : Relation rel;
830 : ListCell *lc;
831 :
832 663 : if (properties->all)
833 : {
834 : Relation attRelation;
835 : SysScanDesc scan;
836 : ScanKeyData key[1];
837 : HeapTuple attributeTuple;
838 :
839 286 : attRelation = table_open(AttributeRelationId, RowShareLock);
840 286 : ScanKeyInit(&key[0],
841 : Anum_pg_attribute_attrelid,
842 : BTEqualStrategyNumber, F_OIDEQ,
843 : ObjectIdGetDatum(pgerelid));
844 286 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
845 : true, NULL, 1, key);
846 2758 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
847 : {
848 2472 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
849 : ColumnRef *cr;
850 : ResTarget *rt;
851 :
852 2472 : if (att->attnum <= 0 || att->attisdropped)
853 1704 : continue;
854 :
855 768 : cr = makeNode(ColumnRef);
856 768 : rt = makeNode(ResTarget);
857 :
858 768 : cr->fields = list_make1(makeString(pstrdup(NameStr(att->attname))));
859 768 : cr->location = -1;
860 :
861 768 : rt->name = pstrdup(NameStr(att->attname));
862 768 : rt->val = (Node *) cr;
863 768 : rt->location = -1;
864 :
865 768 : proplist = lappend(proplist, rt);
866 : }
867 286 : systable_endscan(scan);
868 286 : table_close(attRelation, RowShareLock);
869 : }
870 : else
871 : {
872 377 : proplist = properties->properties;
873 :
874 1011 : foreach(lc, proplist)
875 : {
876 634 : ResTarget *rt = lfirst_node(ResTarget, lc);
877 :
878 634 : if (!rt->name && !IsA(rt->val, ColumnRef))
879 0 : ereport(ERROR,
880 : errcode(ERRCODE_SYNTAX_ERROR),
881 : errmsg("property name required"),
882 : parser_errposition(NULL, rt->location));
883 : }
884 : }
885 :
886 663 : rel = table_open(pgerelid, AccessShareLock);
887 :
888 663 : pstate = make_parsestate(NULL);
889 663 : nsitem = addRangeTableEntryForRelation(pstate,
890 : rel,
891 : AccessShareLock,
892 : NULL,
893 : false,
894 : true);
895 663 : addNSItemToQuery(pstate, nsitem, true, true, true);
896 :
897 663 : table_close(rel, NoLock);
898 :
899 663 : tp = transformTargetList(pstate, proplist, EXPR_KIND_PROPGRAPH_PROPERTY);
900 663 : assign_expr_collations(pstate, (Node *) tp);
901 :
902 2033 : foreach(lc, tp)
903 : {
904 1402 : TargetEntry *te = lfirst_node(TargetEntry, lc);
905 :
906 1402 : insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr);
907 : }
908 631 : }
909 :
910 : /*
911 : * Insert records for a property into the pg_propgraph_property and
912 : * pg_propgraph_label_property catalogs, and register dependencies.
913 : */
914 : static void
915 1402 : insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr)
916 : {
917 : Oid propoid;
918 1402 : Oid exprtypid = exprType((const Node *) expr);
919 1402 : int32 exprtypmod = exprTypmod((const Node *) expr);
920 1402 : Oid exprcollation = exprCollation((const Node *) expr);
921 :
922 : /*
923 : * Insert into pg_propgraph_property if not already existing.
924 : */
925 1402 : propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, Anum_pg_propgraph_property_oid, ObjectIdGetDatum(graphid), CStringGetDatum(propname));
926 1402 : if (!OidIsValid(propoid))
927 : {
928 : Relation rel;
929 : NameData propnamedata;
930 759 : Datum values[Natts_pg_propgraph_property] = {0};
931 759 : bool nulls[Natts_pg_propgraph_property] = {0};
932 : HeapTuple tup;
933 : ObjectAddress myself;
934 : ObjectAddress referenced;
935 :
936 759 : rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock);
937 :
938 759 : propoid = GetNewOidWithIndex(rel, PropgraphPropertyObjectIndexId, Anum_pg_propgraph_property_oid);
939 759 : values[Anum_pg_propgraph_property_oid - 1] = ObjectIdGetDatum(propoid);
940 759 : values[Anum_pg_propgraph_property_pgppgid - 1] = ObjectIdGetDatum(graphid);
941 759 : namestrcpy(&propnamedata, propname);
942 759 : values[Anum_pg_propgraph_property_pgpname - 1] = NameGetDatum(&propnamedata);
943 759 : values[Anum_pg_propgraph_property_pgptypid - 1] = ObjectIdGetDatum(exprtypid);
944 759 : values[Anum_pg_propgraph_property_pgptypmod - 1] = Int32GetDatum(exprtypmod);
945 759 : values[Anum_pg_propgraph_property_pgpcollation - 1] = ObjectIdGetDatum(exprcollation);
946 :
947 759 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
948 759 : CatalogTupleInsert(rel, tup);
949 759 : heap_freetuple(tup);
950 :
951 759 : ObjectAddressSet(myself, PropgraphPropertyRelationId, propoid);
952 :
953 759 : ObjectAddressSet(referenced, RelationRelationId, graphid);
954 759 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
955 759 : ObjectAddressSet(referenced, TypeRelationId, exprtypid);
956 759 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
957 759 : if (OidIsValid(exprcollation) && exprcollation != DEFAULT_COLLATION_OID)
958 : {
959 23 : ObjectAddressSet(referenced, CollationRelationId, exprcollation);
960 23 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
961 : }
962 :
963 759 : table_close(rel, NoLock);
964 : }
965 : else
966 : {
967 643 : HeapTuple pgptup = SearchSysCache1(PROPGRAPHPROPOID, ObjectIdGetDatum(propoid));
968 643 : Form_pg_propgraph_property pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup);
969 643 : Oid proptypid = pgpform->pgptypid;
970 643 : int32 proptypmod = pgpform->pgptypmod;
971 643 : Oid propcollation = pgpform->pgpcollation;
972 :
973 643 : ReleaseSysCache(pgptup);
974 :
975 : /*
976 : * Check that in the graph, all properties with the same name have the
977 : * same type (independent of which label they are on). (See SQL/PGQ
978 : * subclause "Consistency check of a tabular property graph
979 : * descriptor".)
980 : */
981 643 : if (proptypid != exprtypid || proptypmod != exprtypmod)
982 : {
983 16 : ereport(ERROR,
984 : errcode(ERRCODE_SYNTAX_ERROR),
985 : errmsg("property \"%s\" data type mismatch: %s vs. %s",
986 : propname, format_type_with_typemod(proptypid, proptypmod), format_type_with_typemod(exprtypid, exprtypmod)),
987 : errdetail("In a property graph, a property of the same name has to have the same data type in each label."));
988 : }
989 :
990 : /* Similarly for collation */
991 627 : if (propcollation != exprcollation)
992 : {
993 16 : ereport(ERROR,
994 : errcode(ERRCODE_SYNTAX_ERROR),
995 : errmsg("property \"%s\" collation mismatch: %s vs. %s",
996 : propname, get_collation_name(propcollation), get_collation_name(exprcollation)),
997 : errdetail("In a property graph, a property of the same name has to have the same collation in each label."));
998 : }
999 : }
1000 :
1001 : /*
1002 : * Insert into pg_propgraph_label_property
1003 : */
1004 : {
1005 : Relation rel;
1006 1370 : Datum values[Natts_pg_propgraph_label_property] = {0};
1007 1370 : bool nulls[Natts_pg_propgraph_label_property] = {0};
1008 : Oid plpoid;
1009 : HeapTuple tup;
1010 : ObjectAddress myself;
1011 : ObjectAddress referenced;
1012 :
1013 1370 : rel = table_open(PropgraphLabelPropertyRelationId, RowExclusiveLock);
1014 :
1015 1370 : plpoid = GetNewOidWithIndex(rel, PropgraphLabelPropertyObjectIndexId, Anum_pg_propgraph_label_property_oid);
1016 1370 : values[Anum_pg_propgraph_label_property_oid - 1] = ObjectIdGetDatum(plpoid);
1017 1370 : values[Anum_pg_propgraph_label_property_plppropid - 1] = ObjectIdGetDatum(propoid);
1018 1370 : values[Anum_pg_propgraph_label_property_plpellabelid - 1] = ObjectIdGetDatum(ellabeloid);
1019 1370 : values[Anum_pg_propgraph_label_property_plpexpr - 1] = CStringGetTextDatum(nodeToString(expr));
1020 :
1021 1370 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
1022 1370 : CatalogTupleInsert(rel, tup);
1023 1370 : heap_freetuple(tup);
1024 :
1025 1370 : ObjectAddressSet(myself, PropgraphLabelPropertyRelationId, plpoid);
1026 :
1027 1370 : ObjectAddressSet(referenced, PropgraphPropertyRelationId, propoid);
1028 1370 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1029 :
1030 1370 : ObjectAddressSet(referenced, PropgraphElementLabelRelationId, ellabeloid);
1031 1370 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1032 :
1033 1370 : recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false);
1034 :
1035 1370 : table_close(rel, NoLock);
1036 : }
1037 1370 : }
1038 :
1039 : /*
1040 : * Check that for the given graph element, all properties with the same name
1041 : * have the same expression for each label. (See SQL/PGQ subclause "Creation
1042 : * of an element table descriptor".)
1043 : *
1044 : * We check this after all the catalog records are already inserted. This
1045 : * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER
1046 : * PROPERTY GRAPH. We pass in the element OID so that ALTER PROPERTY GRAPH
1047 : * only has to check the element it has just operated on. CREATE PROPERTY
1048 : * GROUP checks all elements it has created.
1049 : */
1050 : static void
1051 483 : check_element_properties(Oid peoid)
1052 : {
1053 : Relation rel1;
1054 : ScanKeyData key1[1];
1055 : SysScanDesc scan1;
1056 : HeapTuple tuple1;
1057 483 : List *propoids = NIL;
1058 483 : List *propexprs = NIL;
1059 :
1060 483 : rel1 = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1061 483 : ScanKeyInit(&key1[0],
1062 : Anum_pg_propgraph_element_label_pgelelid,
1063 : BTEqualStrategyNumber, F_OIDEQ,
1064 : ObjectIdGetDatum(peoid));
1065 :
1066 483 : scan1 = systable_beginscan(rel1, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, key1);
1067 1102 : while (HeapTupleIsValid(tuple1 = systable_getnext(scan1)))
1068 : {
1069 627 : Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple1);
1070 : Relation rel2;
1071 : ScanKeyData key2[1];
1072 : SysScanDesc scan2;
1073 : HeapTuple tuple2;
1074 :
1075 627 : rel2 = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
1076 627 : ScanKeyInit(&key2[0],
1077 : Anum_pg_propgraph_label_property_plpellabelid,
1078 : BTEqualStrategyNumber, F_OIDEQ,
1079 : ObjectIdGetDatum(ellabel->oid));
1080 :
1081 627 : scan2 = systable_beginscan(rel2, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key2);
1082 1965 : while (HeapTupleIsValid(tuple2 = systable_getnext(scan2)))
1083 : {
1084 1346 : Form_pg_propgraph_label_property lprop = (Form_pg_propgraph_label_property) GETSTRUCT(tuple2);
1085 : Oid propoid;
1086 : Datum datum;
1087 : bool isnull;
1088 : char *propexpr;
1089 : ListCell *lc1,
1090 : *lc2;
1091 : bool found;
1092 :
1093 1346 : propoid = lprop->plppropid;
1094 1346 : datum = heap_getattr(tuple2, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(rel2), &isnull);
1095 : Assert(!isnull);
1096 1346 : propexpr = TextDatumGetCString(datum);
1097 :
1098 1346 : found = false;
1099 2643 : forboth(lc1, propoids, lc2, propexprs)
1100 : {
1101 1400 : if (propoid == lfirst_oid(lc1))
1102 : {
1103 : Node *na,
1104 : *nb;
1105 :
1106 103 : na = stringToNode(propexpr);
1107 103 : nb = stringToNode(lfirst(lc2));
1108 :
1109 103 : found = true;
1110 :
1111 103 : if (!equal(na, nb))
1112 : {
1113 : HeapTuple tuple3;
1114 : Form_pg_propgraph_element elform;
1115 : List *dpcontext;
1116 : char *dpa,
1117 : *dpb;
1118 :
1119 8 : tuple3 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peoid));
1120 8 : if (!tuple3)
1121 0 : elog(ERROR, "cache lookup failed for property graph element %u", peoid);
1122 8 : elform = (Form_pg_propgraph_element) GETSTRUCT(tuple3);
1123 8 : dpcontext = deparse_context_for(get_rel_name(elform->pgerelid), elform->pgerelid);
1124 :
1125 8 : dpa = deparse_expression(na, dpcontext, false, false);
1126 8 : dpb = deparse_expression(nb, dpcontext, false, false);
1127 :
1128 : /*
1129 : * show in sorted order to keep output independent of
1130 : * index order
1131 : */
1132 8 : if (strcmp(dpa, dpb) > 0)
1133 : {
1134 : char *tmp;
1135 :
1136 0 : tmp = dpa;
1137 0 : dpa = dpb;
1138 0 : dpb = tmp;
1139 : }
1140 :
1141 8 : ereport(ERROR,
1142 : errcode(ERRCODE_SYNTAX_ERROR),
1143 : errmsg("element \"%s\" property \"%s\" expression mismatch: %s vs. %s",
1144 : NameStr(elform->pgealias), get_propgraph_property_name(propoid), dpa, dpb),
1145 : errdetail("In a property graph element, a property of the same name has to have the same expression in each label."));
1146 :
1147 : ReleaseSysCache(tuple3);
1148 : }
1149 :
1150 95 : break;
1151 : }
1152 : }
1153 :
1154 1338 : if (!found)
1155 : {
1156 1243 : propoids = lappend_oid(propoids, propoid);
1157 1243 : propexprs = lappend(propexprs, propexpr);
1158 : }
1159 : }
1160 619 : systable_endscan(scan2);
1161 619 : table_close(rel2, AccessShareLock);
1162 : }
1163 :
1164 475 : systable_endscan(scan1);
1165 475 : table_close(rel1, AccessShareLock);
1166 475 : }
1167 :
1168 : /*
1169 : * Check that for the given element label, all labels of the same name in the
1170 : * graph have the same number and names of properties (independent of which
1171 : * element they are on). (See SQL/PGQ subclause "Consistency check of a
1172 : * tabular property graph descriptor".)
1173 : *
1174 : * We check this after all the catalog records are already inserted. This
1175 : * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER
1176 : * PROPERTY GRAPH. We pass in the element label OID so that some variants of
1177 : * ALTER PROPERTY GRAPH only have to check the element label it has just
1178 : * operated on. CREATE PROPERTY GRAPH and other ALTER PROPERTY GRAPH variants
1179 : * check all labels.
1180 : */
1181 : static void
1182 955 : check_element_label_properties(Oid ellabeloid)
1183 : {
1184 : Relation rel;
1185 : SysScanDesc scan;
1186 : ScanKeyData key[1];
1187 : HeapTuple tuple;
1188 955 : Oid labelid = InvalidOid;
1189 955 : Oid ref_ellabeloid = InvalidOid;
1190 : List *myprops,
1191 : *refprops;
1192 : List *diff1,
1193 : *diff2;
1194 :
1195 955 : rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1196 :
1197 : /*
1198 : * Get element label info
1199 : */
1200 955 : ScanKeyInit(&key[0],
1201 : Anum_pg_propgraph_element_label_oid,
1202 : BTEqualStrategyNumber,
1203 : F_OIDEQ, ObjectIdGetDatum(ellabeloid));
1204 955 : scan = systable_beginscan(rel, PropgraphElementLabelObjectIndexId, true, NULL, 1, key);
1205 955 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1206 : {
1207 955 : Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
1208 :
1209 955 : labelid = ellabel->pgellabelid;
1210 : }
1211 955 : systable_endscan(scan);
1212 955 : if (!labelid)
1213 0 : elog(ERROR, "element label %u not found", ellabeloid);
1214 :
1215 : /*
1216 : * Find a reference element label to fetch label properties. The
1217 : * reference element label has to have the label OID as the one being
1218 : * checked but be distinct from the one being checked.
1219 : */
1220 955 : ScanKeyInit(&key[0],
1221 : Anum_pg_propgraph_element_label_pgellabelid,
1222 : BTEqualStrategyNumber,
1223 : F_OIDEQ, ObjectIdGetDatum(labelid));
1224 955 : scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
1225 1585 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1226 : {
1227 1084 : Form_pg_propgraph_element_label otherellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
1228 :
1229 1084 : if (otherellabel->oid != ellabeloid)
1230 : {
1231 454 : ref_ellabeloid = otherellabel->oid;
1232 454 : break;
1233 : }
1234 : }
1235 955 : systable_endscan(scan);
1236 :
1237 955 : table_close(rel, AccessShareLock);
1238 :
1239 : /*
1240 : * If there is not previous definition of this label, then we are done.
1241 : */
1242 955 : if (!ref_ellabeloid)
1243 501 : return;
1244 :
1245 : /*
1246 : * Now check number and names.
1247 : *
1248 : * XXX We could provide more detail in the error messages, but that would
1249 : * probably only be useful for some ALTER commands, because otherwise it's
1250 : * not really clear which label definition is the wrong one, and so you'd
1251 : * have to construct a rather verbose report to be of any use. Let's keep
1252 : * it simple for now.
1253 : */
1254 :
1255 454 : myprops = get_element_label_property_names(ellabeloid);
1256 454 : refprops = get_element_label_property_names(ref_ellabeloid);
1257 :
1258 454 : if (list_length(refprops) != list_length(myprops))
1259 16 : ereport(ERROR,
1260 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1261 : errmsg("mismatching number of properties in definition of label \"%s\"", get_propgraph_label_name(labelid)));
1262 :
1263 438 : diff1 = list_difference(myprops, refprops);
1264 438 : diff2 = list_difference(refprops, myprops);
1265 :
1266 438 : if (diff1 || diff2)
1267 8 : ereport(ERROR,
1268 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1269 : errmsg("mismatching properties names in definition of label \"%s\"", get_propgraph_label_name(labelid)));
1270 : }
1271 :
1272 : /*
1273 : * As above, but check all labels of a graph.
1274 : */
1275 : static void
1276 189 : check_all_labels_properties(Oid pgrelid)
1277 : {
1278 960 : foreach_oid(labeloid, get_graph_label_ids(pgrelid))
1279 : {
1280 2103 : foreach_oid(ellabeloid, get_label_element_label_ids(labeloid))
1281 : {
1282 915 : check_element_label_properties(ellabeloid);
1283 : }
1284 : }
1285 177 : }
1286 :
1287 : /*
1288 : * ALTER PROPERTY GRAPH
1289 : */
1290 : ObjectAddress
1291 124 : AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt)
1292 : {
1293 : Oid pgrelid;
1294 : ListCell *lc;
1295 : ObjectAddress pgaddress;
1296 :
1297 124 : pgrelid = RangeVarGetRelidExtended(stmt->pgname,
1298 : ShareRowExclusiveLock,
1299 124 : stmt->missing_ok ? RVR_MISSING_OK : 0,
1300 : RangeVarCallbackOwnsRelation,
1301 : NULL);
1302 120 : if (pgrelid == InvalidOid)
1303 : {
1304 0 : ereport(NOTICE,
1305 : (errmsg("relation \"%s\" does not exist, skipping",
1306 : stmt->pgname->relname)));
1307 0 : return InvalidObjectAddress;
1308 : }
1309 :
1310 120 : ObjectAddressSet(pgaddress, RelationRelationId, pgrelid);
1311 :
1312 136 : foreach(lc, stmt->add_vertex_tables)
1313 : {
1314 32 : PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
1315 : struct element_info *vinfo;
1316 : Relation rel;
1317 : Oid peoid;
1318 :
1319 32 : vinfo = palloc0_object(struct element_info);
1320 32 : vinfo->kind = PGEKIND_VERTEX;
1321 :
1322 32 : vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
1323 :
1324 32 : rel = table_open(vinfo->relid, NoLock);
1325 :
1326 32 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP)
1327 4 : ereport(ERROR,
1328 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1329 : errmsg("cannot add temporary element table to non-temporary property graph"),
1330 : errdetail("Table \"%s\" is a temporary table.", get_rel_name(vinfo->relid)),
1331 : parser_errposition(pstate, vertex->vtable->location)));
1332 :
1333 28 : if (vertex->vtable->alias)
1334 4 : vinfo->aliasname = vertex->vtable->alias->aliasname;
1335 : else
1336 24 : vinfo->aliasname = vertex->vtable->relname;
1337 :
1338 28 : vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location);
1339 :
1340 28 : vinfo->labels = vertex->labels;
1341 :
1342 28 : table_close(rel, NoLock);
1343 :
1344 28 : if (SearchSysCacheExists2(PROPGRAPHELALIAS,
1345 : ObjectIdGetDatum(pgrelid),
1346 : CStringGetDatum(vinfo->aliasname)))
1347 4 : ereport(ERROR,
1348 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1349 : errmsg("alias \"%s\" already exists in property graph \"%s\"",
1350 : vinfo->aliasname, stmt->pgname->relname),
1351 : parser_errposition(pstate, vertex->vtable->location));
1352 :
1353 24 : peoid = insert_element_record(pgaddress, vinfo);
1354 :
1355 20 : CommandCounterIncrement();
1356 20 : check_element_properties(peoid);
1357 16 : check_all_labels_properties(pgrelid);
1358 : }
1359 :
1360 136 : foreach(lc, stmt->add_edge_tables)
1361 : {
1362 36 : PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc);
1363 : struct element_info *einfo;
1364 : Relation rel;
1365 : Relation srcrel;
1366 : Relation destrel;
1367 : Oid peoid;
1368 :
1369 36 : einfo = palloc0_object(struct element_info);
1370 36 : einfo->kind = PGEKIND_EDGE;
1371 :
1372 36 : einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
1373 :
1374 36 : rel = table_open(einfo->relid, NoLock);
1375 :
1376 36 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP)
1377 0 : ereport(ERROR,
1378 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1379 : errmsg("cannot add temporary element table to non-temporary property graph"),
1380 : errdetail("Table \"%s\" is a temporary table.", get_rel_name(einfo->relid)),
1381 : parser_errposition(pstate, edge->etable->location)));
1382 :
1383 36 : if (edge->etable->alias)
1384 0 : einfo->aliasname = edge->etable->alias->aliasname;
1385 : else
1386 36 : einfo->aliasname = edge->etable->relname;
1387 :
1388 36 : einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location);
1389 :
1390 36 : einfo->srcvertexid = get_vertex_oid(pstate, pgrelid, edge->esrcvertex, edge->location);
1391 36 : einfo->destvertexid = get_vertex_oid(pstate, pgrelid, edge->edestvertex, edge->location);
1392 :
1393 36 : einfo->srcrelid = get_element_relid(einfo->srcvertexid);
1394 36 : einfo->destrelid = get_element_relid(einfo->destvertexid);
1395 :
1396 36 : srcrel = table_open(einfo->srcrelid, AccessShareLock);
1397 36 : destrel = table_open(einfo->destrelid, AccessShareLock);
1398 :
1399 36 : propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel,
1400 36 : einfo->aliasname, edge->location, "SOURCE",
1401 : &einfo->srckey, &einfo->srcref, &einfo->srceqop);
1402 36 : propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel,
1403 36 : einfo->aliasname, edge->location, "DESTINATION",
1404 : &einfo->destkey, &einfo->destref, &einfo->desteqop);
1405 :
1406 36 : einfo->labels = edge->labels;
1407 :
1408 36 : table_close(destrel, NoLock);
1409 36 : table_close(srcrel, NoLock);
1410 :
1411 36 : table_close(rel, NoLock);
1412 :
1413 36 : if (SearchSysCacheExists2(PROPGRAPHELALIAS,
1414 : ObjectIdGetDatum(pgrelid),
1415 : CStringGetDatum(einfo->aliasname)))
1416 0 : ereport(ERROR,
1417 : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1418 : errmsg("alias \"%s\" already exists in property graph \"%s\"",
1419 : einfo->aliasname, stmt->pgname->relname),
1420 : parser_errposition(pstate, edge->etable->location));
1421 :
1422 36 : peoid = insert_element_record(pgaddress, einfo);
1423 :
1424 32 : CommandCounterIncrement();
1425 32 : check_element_properties(peoid);
1426 32 : check_all_labels_properties(pgrelid);
1427 : }
1428 :
1429 104 : foreach(lc, stmt->drop_vertex_tables)
1430 : {
1431 8 : char *alias = strVal(lfirst(lc));
1432 : Oid peoid;
1433 : ObjectAddress obj;
1434 :
1435 8 : peoid = get_vertex_oid(pstate, pgrelid, alias, -1);
1436 8 : ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
1437 8 : performDeletion(&obj, stmt->drop_behavior, 0);
1438 : }
1439 :
1440 108 : foreach(lc, stmt->drop_edge_tables)
1441 : {
1442 12 : char *alias = strVal(lfirst(lc));
1443 : Oid peoid;
1444 : ObjectAddress obj;
1445 :
1446 12 : peoid = get_edge_oid(pstate, pgrelid, alias, -1);
1447 12 : ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
1448 12 : performDeletion(&obj, stmt->drop_behavior, 0);
1449 : }
1450 :
1451 : /* Remove any orphaned pg_propgraph_label entries */
1452 96 : if (stmt->drop_vertex_tables || stmt->drop_edge_tables)
1453 : {
1454 88 : foreach_oid(labeloid, get_graph_label_ids(pgrelid))
1455 : {
1456 64 : if (!get_label_element_label_ids(labeloid))
1457 : {
1458 : ObjectAddress obj;
1459 :
1460 12 : ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid);
1461 12 : performDeletion(&obj, stmt->drop_behavior, 0);
1462 : }
1463 : }
1464 : }
1465 :
1466 108 : foreach(lc, stmt->add_labels)
1467 : {
1468 28 : PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
1469 : Oid peoid;
1470 : Oid pgerelid;
1471 : Oid ellabeloid;
1472 :
1473 : Assert(lp->label);
1474 :
1475 28 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1476 28 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1477 : else
1478 0 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1479 :
1480 28 : pgerelid = get_element_relid(peoid);
1481 :
1482 28 : ellabeloid = insert_label_record(pgrelid, peoid, lp->label);
1483 28 : insert_property_records(pgrelid, ellabeloid, pgerelid, lp->properties);
1484 :
1485 24 : CommandCounterIncrement();
1486 24 : check_element_properties(peoid);
1487 24 : check_element_label_properties(ellabeloid);
1488 : }
1489 :
1490 80 : if (stmt->drop_label)
1491 : {
1492 : Oid peoid;
1493 : Oid labeloid;
1494 : Oid ellabeloid;
1495 : ObjectAddress obj;
1496 :
1497 12 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1498 12 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1499 : else
1500 0 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1501 :
1502 12 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
1503 : Anum_pg_propgraph_label_oid,
1504 : ObjectIdGetDatum(pgrelid),
1505 : CStringGetDatum(stmt->drop_label));
1506 12 : if (!labeloid)
1507 4 : ereport(ERROR,
1508 : errcode(ERRCODE_UNDEFINED_OBJECT),
1509 : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1510 : get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
1511 : parser_errposition(pstate, -1));
1512 :
1513 8 : ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
1514 : Anum_pg_propgraph_element_label_oid,
1515 : ObjectIdGetDatum(peoid),
1516 : ObjectIdGetDatum(labeloid));
1517 :
1518 8 : if (!ellabeloid)
1519 0 : ereport(ERROR,
1520 : errcode(ERRCODE_UNDEFINED_OBJECT),
1521 : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1522 : get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
1523 : parser_errposition(pstate, -1));
1524 :
1525 8 : ObjectAddressSet(obj, PropgraphElementLabelRelationId, ellabeloid);
1526 8 : performDeletion(&obj, stmt->drop_behavior, 0);
1527 :
1528 : /* Remove any orphaned pg_propgraph_label entries */
1529 8 : if (!get_label_element_label_ids(labeloid))
1530 : {
1531 4 : ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid);
1532 4 : performDeletion(&obj, stmt->drop_behavior, 0);
1533 : }
1534 : }
1535 :
1536 76 : if (stmt->add_properties)
1537 : {
1538 : Oid peoid;
1539 : Oid pgerelid;
1540 : Oid labeloid;
1541 : Oid ellabeloid;
1542 :
1543 8 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1544 4 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1545 : else
1546 4 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1547 :
1548 8 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
1549 : Anum_pg_propgraph_label_oid,
1550 : ObjectIdGetDatum(pgrelid),
1551 : CStringGetDatum(stmt->alter_label));
1552 8 : if (!labeloid)
1553 0 : ereport(ERROR,
1554 : errcode(ERRCODE_UNDEFINED_OBJECT),
1555 : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1556 : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
1557 : parser_errposition(pstate, -1));
1558 :
1559 8 : ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
1560 : Anum_pg_propgraph_element_label_oid,
1561 : ObjectIdGetDatum(peoid),
1562 : ObjectIdGetDatum(labeloid));
1563 8 : if (!ellabeloid)
1564 0 : ereport(ERROR,
1565 : errcode(ERRCODE_UNDEFINED_OBJECT),
1566 : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1567 : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
1568 : parser_errposition(pstate, -1));
1569 :
1570 8 : pgerelid = get_element_relid(peoid);
1571 :
1572 8 : insert_property_records(pgrelid, ellabeloid, pgerelid, stmt->add_properties);
1573 :
1574 8 : CommandCounterIncrement();
1575 8 : check_element_properties(peoid);
1576 8 : check_element_label_properties(ellabeloid);
1577 : }
1578 :
1579 76 : if (stmt->drop_properties)
1580 : {
1581 : Oid peoid;
1582 : Oid labeloid;
1583 : Oid ellabeloid;
1584 : ObjectAddress obj;
1585 :
1586 8 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1587 4 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1588 : else
1589 4 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1590 :
1591 8 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
1592 : Anum_pg_propgraph_label_oid,
1593 : ObjectIdGetDatum(pgrelid),
1594 : CStringGetDatum(stmt->alter_label));
1595 8 : if (!labeloid)
1596 0 : ereport(ERROR,
1597 : errcode(ERRCODE_UNDEFINED_OBJECT),
1598 : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1599 : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
1600 : parser_errposition(pstate, -1));
1601 :
1602 8 : ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
1603 : Anum_pg_propgraph_element_label_oid,
1604 : ObjectIdGetDatum(peoid),
1605 : ObjectIdGetDatum(labeloid));
1606 :
1607 8 : if (!ellabeloid)
1608 0 : ereport(ERROR,
1609 : errcode(ERRCODE_UNDEFINED_OBJECT),
1610 : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1611 : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
1612 : parser_errposition(pstate, -1));
1613 :
1614 16 : foreach(lc, stmt->drop_properties)
1615 : {
1616 8 : char *propname = strVal(lfirst(lc));
1617 : Oid propoid;
1618 : Oid plpoid;
1619 :
1620 8 : propoid = GetSysCacheOid2(PROPGRAPHPROPNAME,
1621 : Anum_pg_propgraph_property_oid,
1622 : ObjectIdGetDatum(pgrelid),
1623 : CStringGetDatum(propname));
1624 8 : if (!propoid)
1625 0 : ereport(ERROR,
1626 : errcode(ERRCODE_UNDEFINED_OBJECT),
1627 : errmsg("property graph \"%s\" element \"%s\" label \"%s\" has no property \"%s\"",
1628 : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label, propname),
1629 : parser_errposition(pstate, -1));
1630 :
1631 8 : plpoid = GetSysCacheOid2(PROPGRAPHLABELPROP, Anum_pg_propgraph_label_property_oid, ObjectIdGetDatum(ellabeloid), ObjectIdGetDatum(propoid));
1632 :
1633 8 : ObjectAddressSet(obj, PropgraphLabelPropertyRelationId, plpoid);
1634 8 : performDeletion(&obj, stmt->drop_behavior, 0);
1635 : }
1636 :
1637 8 : check_element_label_properties(ellabeloid);
1638 : }
1639 :
1640 : /* Remove any orphaned pg_propgraph_property entries */
1641 76 : if (stmt->drop_properties || stmt->drop_vertex_tables || stmt->drop_edge_tables)
1642 : {
1643 200 : foreach_oid(propoid, get_graph_property_ids(pgrelid))
1644 : {
1645 : Relation rel;
1646 : SysScanDesc scan;
1647 : ScanKeyData key[1];
1648 :
1649 160 : rel = table_open(PropgraphLabelPropertyRelationId, RowShareLock);
1650 160 : ScanKeyInit(&key[0],
1651 : Anum_pg_propgraph_label_property_plppropid,
1652 : BTEqualStrategyNumber, F_OIDEQ,
1653 : ObjectIdGetDatum(propoid));
1654 : /* XXX no suitable index */
1655 160 : scan = systable_beginscan(rel, InvalidOid, true, NULL, 1, key);
1656 160 : if (!systable_getnext(scan))
1657 : {
1658 : ObjectAddress obj;
1659 :
1660 20 : ObjectAddressSet(obj, PropgraphPropertyRelationId, propoid);
1661 20 : performDeletion(&obj, stmt->drop_behavior, 0);
1662 : }
1663 :
1664 160 : systable_endscan(scan);
1665 160 : table_close(rel, RowShareLock);
1666 : }
1667 : }
1668 :
1669 : /*
1670 : * Invalidate relcache entry of the property graph so that the queries in
1671 : * the cached plans referencing the property graph will be rewritten
1672 : * considering changes to the propert graph.
1673 : */
1674 76 : CacheInvalidateRelcacheByRelid(pgrelid);
1675 :
1676 76 : return pgaddress;
1677 : }
1678 :
1679 : /*
1680 : * Get OID of vertex from graph OID and element alias. Element must be a
1681 : * vertex, otherwise error.
1682 : */
1683 : static Oid
1684 128 : get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
1685 : {
1686 : HeapTuple tuple;
1687 : Oid peoid;
1688 :
1689 128 : tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
1690 128 : if (!tuple)
1691 0 : ereport(ERROR,
1692 : errcode(ERRCODE_UNDEFINED_OBJECT),
1693 : errmsg("property graph \"%s\" has no element with alias \"%s\"",
1694 : get_rel_name(pgrelid), alias),
1695 : parser_errposition(pstate, location));
1696 :
1697 128 : if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_VERTEX)
1698 0 : ereport(ERROR,
1699 : errcode(ERRCODE_SYNTAX_ERROR),
1700 : errmsg("element \"%s\" of property graph \"%s\" is not a vertex",
1701 : alias, get_rel_name(pgrelid)),
1702 : parser_errposition(pstate, location));
1703 :
1704 128 : peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
1705 :
1706 128 : ReleaseSysCache(tuple);
1707 :
1708 128 : return peoid;
1709 : }
1710 :
1711 : /*
1712 : * Get OID of edge from graph OID and element alias. Element must be an edge,
1713 : * otherwise error.
1714 : */
1715 : static Oid
1716 20 : get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
1717 : {
1718 : HeapTuple tuple;
1719 : Oid peoid;
1720 :
1721 20 : tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
1722 20 : if (!tuple)
1723 0 : ereport(ERROR,
1724 : errcode(ERRCODE_UNDEFINED_OBJECT),
1725 : errmsg("property graph \"%s\" has no element with alias \"%s\"",
1726 : get_rel_name(pgrelid), alias),
1727 : parser_errposition(pstate, location));
1728 :
1729 20 : if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_EDGE)
1730 0 : ereport(ERROR,
1731 : errcode(ERRCODE_SYNTAX_ERROR),
1732 : errmsg("element \"%s\" of property graph \"%s\" is not an edge",
1733 : alias, get_rel_name(pgrelid)),
1734 : parser_errposition(pstate, location));
1735 :
1736 20 : peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
1737 :
1738 20 : ReleaseSysCache(tuple);
1739 :
1740 20 : return peoid;
1741 : }
1742 :
1743 : /*
1744 : * Get the element table relation OID from the OID of the element.
1745 : */
1746 : static Oid
1747 108 : get_element_relid(Oid peid)
1748 : {
1749 : HeapTuple tuple;
1750 : Oid pgerelid;
1751 :
1752 108 : tuple = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peid));
1753 108 : if (!tuple)
1754 0 : elog(ERROR, "cache lookup failed for property graph element %u", peid);
1755 :
1756 108 : pgerelid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgerelid;
1757 :
1758 108 : ReleaseSysCache(tuple);
1759 :
1760 108 : return pgerelid;
1761 : }
1762 :
1763 : /*
1764 : * Get a list of all label OIDs of a graph.
1765 : */
1766 : static List *
1767 201 : get_graph_label_ids(Oid graphid)
1768 : {
1769 : Relation rel;
1770 : SysScanDesc scan;
1771 : ScanKeyData key[1];
1772 : HeapTuple tuple;
1773 201 : List *result = NIL;
1774 :
1775 201 : rel = table_open(PropgraphLabelRelationId, AccessShareLock);
1776 201 : ScanKeyInit(&key[0],
1777 : Anum_pg_propgraph_label_pglpgid,
1778 : BTEqualStrategyNumber,
1779 : F_OIDEQ, ObjectIdGetDatum(graphid));
1780 201 : scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId, true, NULL, 1, key);
1781 871 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1782 : {
1783 670 : result = lappend_oid(result, ((Form_pg_propgraph_label) GETSTRUCT(tuple))->oid);
1784 : }
1785 201 : systable_endscan(scan);
1786 201 : table_close(rel, AccessShareLock);
1787 :
1788 201 : return result;
1789 : }
1790 :
1791 : /*
1792 : * Get a list of all element label OIDs for a label.
1793 : */
1794 : static List *
1795 678 : get_label_element_label_ids(Oid labelid)
1796 : {
1797 : Relation rel;
1798 : SysScanDesc scan;
1799 : ScanKeyData key[1];
1800 : HeapTuple tuple;
1801 678 : List *result = NIL;
1802 :
1803 678 : rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1804 678 : ScanKeyInit(&key[0],
1805 : Anum_pg_propgraph_element_label_pgellabelid,
1806 : BTEqualStrategyNumber,
1807 : F_OIDEQ, ObjectIdGetDatum(labelid));
1808 678 : scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
1809 1721 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1810 : {
1811 1043 : result = lappend_oid(result, ((Form_pg_propgraph_element_label) GETSTRUCT(tuple))->oid);
1812 : }
1813 678 : systable_endscan(scan);
1814 678 : table_close(rel, AccessShareLock);
1815 :
1816 678 : return result;
1817 : }
1818 :
1819 : /*
1820 : * Get the names of properties associated with the given element label OID.
1821 : *
1822 : * The result is a list of String nodes (so we can use list functions to
1823 : * detect differences).
1824 : */
1825 : static List *
1826 908 : get_element_label_property_names(Oid ellabeloid)
1827 : {
1828 : Relation rel;
1829 : SysScanDesc scan;
1830 : ScanKeyData key[1];
1831 : HeapTuple tuple;
1832 908 : List *result = NIL;
1833 :
1834 908 : rel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
1835 :
1836 908 : ScanKeyInit(&key[0],
1837 : Anum_pg_propgraph_label_property_plpellabelid,
1838 : BTEqualStrategyNumber, F_OIDEQ,
1839 : ObjectIdGetDatum(ellabeloid));
1840 :
1841 908 : scan = systable_beginscan(rel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key);
1842 :
1843 2350 : while ((tuple = systable_getnext(scan)))
1844 : {
1845 1442 : Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tuple);
1846 :
1847 1442 : result = lappend(result, makeString(get_propgraph_property_name(plpform->plppropid)));
1848 : }
1849 :
1850 908 : systable_endscan(scan);
1851 908 : table_close(rel, AccessShareLock);
1852 :
1853 908 : return result;
1854 : }
1855 :
1856 : /*
1857 : * Get a list of all property OIDs of a graph.
1858 : */
1859 : static List *
1860 20 : get_graph_property_ids(Oid graphid)
1861 : {
1862 : Relation rel;
1863 : SysScanDesc scan;
1864 : ScanKeyData key[1];
1865 : HeapTuple tuple;
1866 20 : List *result = NIL;
1867 :
1868 20 : rel = table_open(PropgraphPropertyRelationId, AccessShareLock);
1869 20 : ScanKeyInit(&key[0],
1870 : Anum_pg_propgraph_property_pgppgid,
1871 : BTEqualStrategyNumber,
1872 : F_OIDEQ, ObjectIdGetDatum(graphid));
1873 20 : scan = systable_beginscan(rel, PropgraphPropertyNameIndexId, true, NULL, 1, key);
1874 180 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1875 : {
1876 160 : result = lappend_oid(result, ((Form_pg_propgraph_property) GETSTRUCT(tuple))->oid);
1877 : }
1878 20 : systable_endscan(scan);
1879 20 : table_close(rel, AccessShareLock);
1880 :
1881 20 : return result;
1882 : }
|