Branch data 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 *key_clause, 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 : 210 : CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt)
105 : : {
106 : 210 : CreateStmt *cstmt = makeNode(CreateStmt);
107 : : char components_persistence;
108 : : ListCell *lc;
109 : : ObjectAddress pgaddress;
110 : 210 : List *vertex_infos = NIL;
111 : 210 : List *edge_infos = NIL;
112 : 210 : List *element_aliases = NIL;
113 : 210 : List *element_oids = NIL;
114 : :
115 [ + + ]: 210 : 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 : 206 : components_persistence = RELPERSISTENCE_PERMANENT;
121 : :
122 [ + + + + : 561 : foreach(lc, stmt->vertex_tables)
+ + ]
123 : : {
124 : 363 : PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
125 : : struct element_info *vinfo;
126 : : Relation rel;
127 : :
128 : 363 : vinfo = palloc0_object(struct element_info);
129 : 363 : vinfo->kind = PGEKIND_VERTEX;
130 : :
131 : 363 : vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
132 : :
133 : 359 : rel = table_open(vinfo->relid, NoLock);
134 : :
135 [ + + ]: 359 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
136 : 8 : components_persistence = RELPERSISTENCE_TEMP;
137 : :
138 [ + + ]: 359 : if (vertex->vtable->alias)
139 : 8 : vinfo->aliasname = vertex->vtable->alias->aliasname;
140 : : else
141 : 351 : vinfo->aliasname = vertex->vtable->relname;
142 : :
143 [ + + ]: 359 : 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 : 355 : vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location);
150 : :
151 : 355 : vinfo->labels = vertex->labels;
152 : :
153 : 355 : table_close(rel, NoLock);
154 : :
155 : 355 : vertex_infos = lappend(vertex_infos, vinfo);
156 : :
157 : 355 : element_aliases = lappend(element_aliases, makeString(vinfo->aliasname));
158 : : }
159 : :
160 [ + + + + : 359 : foreach(lc, stmt->edge_tables)
+ + ]
161 : : {
162 : 181 : 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 : 181 : einfo = palloc0_object(struct element_info);
172 : 181 : einfo->kind = PGEKIND_EDGE;
173 : :
174 : 181 : einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
175 : :
176 : 181 : rel = table_open(einfo->relid, NoLock);
177 : :
178 [ - + ]: 181 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
179 : 0 : components_persistence = RELPERSISTENCE_TEMP;
180 : :
181 [ - + ]: 181 : if (edge->etable->alias)
182 : 0 : einfo->aliasname = edge->etable->alias->aliasname;
183 : : else
184 : 181 : einfo->aliasname = edge->etable->relname;
185 : :
186 [ - + ]: 181 : 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 : 181 : einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location);
193 : :
194 : 181 : einfo->srcvertex = edge->esrcvertex;
195 : 181 : einfo->destvertex = edge->edestvertex;
196 : :
197 : 181 : srcrelid = 0;
198 : 181 : destrelid = 0;
199 [ + - + + : 431 : foreach(lc2, vertex_infos)
+ + ]
200 : : {
201 : 423 : struct element_info *vinfo = lfirst(lc2);
202 : :
203 [ + + ]: 423 : if (strcmp(vinfo->aliasname, edge->esrcvertex) == 0)
204 : 177 : srcrelid = vinfo->relid;
205 : :
206 [ + + ]: 423 : if (strcmp(vinfo->aliasname, edge->edestvertex) == 0)
207 : 177 : destrelid = vinfo->relid;
208 : :
209 [ + + + + ]: 423 : if (srcrelid && destrelid)
210 : 173 : break;
211 : : }
212 [ + + ]: 181 : 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 [ + + ]: 177 : 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 : 173 : srcrel = table_open(srcrelid, NoLock);
226 : 173 : destrel = table_open(destrelid, NoLock);
227 : :
228 : 173 : propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel,
229 : 173 : einfo->aliasname, edge->location, "SOURCE",
230 : : &einfo->srckey, &einfo->srcref, &einfo->srceqop);
231 : 165 : propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel,
232 : 165 : einfo->aliasname, edge->location, "DESTINATION",
233 : : &einfo->destkey, &einfo->destref, &einfo->desteqop);
234 : :
235 : 161 : einfo->labels = edge->labels;
236 : :
237 : 161 : table_close(destrel, NoLock);
238 : 161 : table_close(srcrel, NoLock);
239 : :
240 : 161 : table_close(rel, NoLock);
241 : :
242 : 161 : edge_infos = lappend(edge_infos, einfo);
243 : :
244 : 161 : element_aliases = lappend(element_aliases, makeString(einfo->aliasname));
245 : : }
246 : :
247 : 178 : cstmt->relation = stmt->pgname;
248 : 178 : cstmt->oncommit = ONCOMMIT_NOOP;
249 : :
250 : : /*
251 : : * Automatically make it temporary if any component tables are temporary
252 : : * (see also DefineView()).
253 : : */
254 [ + + ]: 178 : if (stmt->pgname->relpersistence == RELPERSISTENCE_PERMANENT
255 [ + + ]: 166 : && 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 : 178 : pgaddress = DefineRelation(cstmt, RELKIND_PROPGRAPH, InvalidOid, NULL, NULL);
265 : :
266 [ + + + + : 465 : foreach(lc, vertex_infos)
+ + ]
267 : : {
268 : 307 : struct element_info *vinfo = lfirst(lc);
269 : : Oid peoid;
270 : :
271 : 307 : peoid = insert_element_record(pgaddress, vinfo);
272 : 291 : element_oids = lappend_oid(element_oids, peoid);
273 : : }
274 : :
275 [ + + + + : 315 : foreach(lc, edge_infos)
+ + ]
276 : : {
277 : 161 : 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 [ + - + - : 383 : foreach(lc2, vertex_infos)
+ - ]
286 : : {
287 : 383 : struct element_info *vinfo = lfirst(lc2);
288 : :
289 [ + + ]: 383 : if (strcmp(vinfo->aliasname, einfo->srcvertex) == 0)
290 : : {
291 : 161 : einfo->srcvertexid = vinfo->elementid;
292 : 161 : einfo->srcrelid = vinfo->relid;
293 : : }
294 [ + + ]: 383 : if (strcmp(vinfo->aliasname, einfo->destvertex) == 0)
295 : : {
296 : 161 : einfo->destvertexid = vinfo->elementid;
297 : 161 : einfo->destrelid = vinfo->relid;
298 : : }
299 [ + + + + ]: 383 : if (einfo->srcvertexid && einfo->destvertexid)
300 : 161 : break;
301 : : }
302 : : Assert(einfo->srcvertexid);
303 : : Assert(einfo->destvertexid);
304 : : Assert(einfo->srcrelid);
305 : : Assert(einfo->destrelid);
306 : 161 : peoid = insert_element_record(pgaddress, einfo);
307 : 157 : element_oids = lappend_oid(element_oids, peoid);
308 : : }
309 : :
310 : 154 : CommandCounterIncrement();
311 : :
312 [ + + + + : 716 : foreach_oid(peoid, element_oids)
+ + ]
313 : 416 : check_element_properties(peoid);
314 : 150 : check_all_labels_properties(pgaddress.objectId);
315 : :
316 : 138 : 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 : 600 : propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, const char *aliasname, int location)
326 : : {
327 : : ArrayType *a;
328 : :
329 [ + + ]: 600 : if (key_clause == NIL)
330 : : {
331 : 128 : Oid pkidx = RelationGetPrimaryKeyIndex(element_rel, false);
332 : :
333 [ - + ]: 128 : 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 : 128 : indexDesc = index_open(pkidx, AccessShareLock);
343 : 128 : a = array_from_attnums(indexDesc->rd_index->indkey.dim1, indexDesc->rd_index->indkey.values);
344 : 128 : index_close(indexDesc, NoLock);
345 : : }
346 : : }
347 : : else
348 : : {
349 : 472 : a = array_from_attnums(list_length(key_clause),
350 : 472 : array_from_column_list(pstate, key_clause, location, element_rel));
351 : : }
352 : :
353 : 600 : 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 : 410 : 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 [ + + ]: 410 : 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 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 vertices.
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 : : * vertices. 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 : 20 : ForeignKeyCacheInfo *fk = NULL;
479 : :
480 [ + + + + : 72 : foreach_node(ForeignKeyCacheInfo, tmp, RelationGetFKeyList(edge_rel))
+ + ]
481 : : {
482 [ + + ]: 32 : if (tmp->confrelid == RelationGetRelid(ref_rel))
483 : : {
484 [ - + ]: 16 : 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 : 16 : fk = tmp;
490 : : }
491 : : }
492 : :
493 [ + + ]: 20 : 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 : 16 : nkeys = fk->nkeys;
500 : 16 : keyattnums = fk->conkey;
501 : 16 : refattnums = fk->confkey;
502 : 16 : keyeqops = fk->conpfeqop;
503 : : }
504 : :
505 : 398 : *outkey = array_from_attnums(nkeys, keyattnums);
506 : 398 : *outref = array_from_attnums(nkeys, refattnums);
507 : 398 : datums = palloc_array(Datum, nkeys);
508 [ + + ]: 844 : for (int i = 0; i < nkeys; i++)
509 : 446 : datums[i] = ObjectIdGetDatum(keyeqops[i]);
510 : 398 : *outeqop = construct_array_builtin(datums, nkeys, OIDOID);
511 : 398 : }
512 : :
513 : : /*
514 : : * Convert list of column names in the specified relation into an array of
515 : : * column numbers.
516 : : */
517 : : static AttrNumber *
518 : 1252 : 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 : 1252 : numattrs = list_length(colnames);
526 : 1252 : attnums = palloc_array(AttrNumber, numattrs);
527 : :
528 : 1252 : i = 0;
529 [ + - + + : 2761 : foreach(lc, colnames)
+ + ]
530 : : {
531 : 1509 : char *colname = strVal(lfirst(lc));
532 : 1509 : Oid relid = RelationGetRelid(element_rel);
533 : : AttrNumber attnum;
534 : :
535 : 1509 : attnum = get_attnum(relid, colname);
536 [ - + ]: 1509 : 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 : 1509 : attnums[i++] = attnum;
543 : : }
544 : :
545 [ + + ]: 2761 : for (int j = 0; j < numattrs; j++)
546 : : {
547 [ + + ]: 1800 : 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 : 1252 : return attnums;
558 : : }
559 : :
560 : : static ArrayType *
561 : 1396 : array_from_attnums(int numattrs, const AttrNumber *attnums)
562 : : {
563 : : Datum *attnumsd;
564 : :
565 : 1396 : attnumsd = palloc_array(Datum, numattrs);
566 : :
567 [ + + ]: 3081 : for (int i = 0; i < numattrs; i++)
568 : 1685 : attnumsd[i] = Int16GetDatum(attnums[i]);
569 : :
570 : 1396 : return construct_array_builtin(attnumsd, numattrs, INT2OID);
571 : : }
572 : :
573 : : static void
574 : 1316 : array_of_attnums_to_objectaddrs(Oid relid, ArrayType *arr, ObjectAddresses *addrs)
575 : : {
576 : : Datum *attnumsd;
577 : : int numattrs;
578 : :
579 : 1316 : deconstruct_array_builtin(arr, INT2OID, &attnumsd, NULL, &numattrs);
580 : :
581 [ + + ]: 2901 : for (int i = 0; i < numattrs; i++)
582 : : {
583 : : ObjectAddress referenced;
584 : :
585 : 1585 : ObjectAddressSubSet(referenced, RelationRelationId, relid, DatumGetInt16(attnumsd[i]));
586 : 1585 : add_exact_object_address(&referenced, addrs);
587 : : }
588 : 1316 : }
589 : :
590 : : static void
591 : 394 : array_of_opers_to_objectaddrs(ArrayType *arr, ObjectAddresses *addrs)
592 : : {
593 : : Datum *opersd;
594 : : int numopers;
595 : :
596 : 394 : deconstruct_array_builtin(arr, OIDOID, &opersd, NULL, &numopers);
597 : :
598 [ + + ]: 836 : for (int i = 0; i < numopers; i++)
599 : : {
600 : : ObjectAddress referenced;
601 : :
602 : 442 : ObjectAddressSet(referenced, OperatorRelationId, DatumGetObjectId(opersd[i]));
603 : 442 : add_exact_object_address(&referenced, addrs);
604 : : }
605 : 394 : }
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 : 528 : insert_element_record(ObjectAddress pgaddress, struct element_info *einfo)
613 : : {
614 : 528 : Oid graphid = pgaddress.objectId;
615 : : Relation rel;
616 : : NameData aliasname;
617 : : Oid peoid;
618 : 528 : Datum values[Natts_pg_propgraph_element] = {0};
619 : 528 : bool nulls[Natts_pg_propgraph_element] = {0};
620 : : HeapTuple tup;
621 : : ObjectAddress myself;
622 : : ObjectAddress referenced;
623 : : ObjectAddresses *addrs;
624 : :
625 : 528 : rel = table_open(PropgraphElementRelationId, RowExclusiveLock);
626 : :
627 : 528 : peoid = GetNewOidWithIndex(rel, PropgraphElementObjectIndexId, Anum_pg_propgraph_element_oid);
628 : 528 : einfo->elementid = peoid;
629 : 528 : values[Anum_pg_propgraph_element_oid - 1] = ObjectIdGetDatum(peoid);
630 : 528 : values[Anum_pg_propgraph_element_pgepgid - 1] = ObjectIdGetDatum(graphid);
631 : 528 : values[Anum_pg_propgraph_element_pgerelid - 1] = ObjectIdGetDatum(einfo->relid);
632 : 528 : namestrcpy(&aliasname, einfo->aliasname);
633 : 528 : values[Anum_pg_propgraph_element_pgealias - 1] = NameGetDatum(&aliasname);
634 : 528 : values[Anum_pg_propgraph_element_pgekind - 1] = CharGetDatum(einfo->kind);
635 : 528 : values[Anum_pg_propgraph_element_pgesrcvertexid - 1] = ObjectIdGetDatum(einfo->srcvertexid);
636 : 528 : values[Anum_pg_propgraph_element_pgedestvertexid - 1] = ObjectIdGetDatum(einfo->destvertexid);
637 : 528 : values[Anum_pg_propgraph_element_pgekey - 1] = PointerGetDatum(einfo->key);
638 : :
639 [ + + ]: 528 : if (einfo->srckey)
640 : 197 : values[Anum_pg_propgraph_element_pgesrckey - 1] = PointerGetDatum(einfo->srckey);
641 : : else
642 : 331 : nulls[Anum_pg_propgraph_element_pgesrckey - 1] = true;
643 [ + + ]: 528 : if (einfo->srcref)
644 : 197 : values[Anum_pg_propgraph_element_pgesrcref - 1] = PointerGetDatum(einfo->srcref);
645 : : else
646 : 331 : nulls[Anum_pg_propgraph_element_pgesrcref - 1] = true;
647 [ + + ]: 528 : if (einfo->srceqop)
648 : 197 : values[Anum_pg_propgraph_element_pgesrceqop - 1] = PointerGetDatum(einfo->srceqop);
649 : : else
650 : 331 : nulls[Anum_pg_propgraph_element_pgesrceqop - 1] = true;
651 [ + + ]: 528 : if (einfo->destkey)
652 : 197 : values[Anum_pg_propgraph_element_pgedestkey - 1] = PointerGetDatum(einfo->destkey);
653 : : else
654 : 331 : nulls[Anum_pg_propgraph_element_pgedestkey - 1] = true;
655 [ + + ]: 528 : if (einfo->destref)
656 : 197 : values[Anum_pg_propgraph_element_pgedestref - 1] = PointerGetDatum(einfo->destref);
657 : : else
658 : 331 : nulls[Anum_pg_propgraph_element_pgedestref - 1] = true;
659 [ + + ]: 528 : if (einfo->desteqop)
660 : 197 : values[Anum_pg_propgraph_element_pgedesteqop - 1] = PointerGetDatum(einfo->desteqop);
661 : : else
662 : 331 : nulls[Anum_pg_propgraph_element_pgedesteqop - 1] = true;
663 : :
664 : 528 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
665 : 528 : CatalogTupleInsert(rel, tup);
666 : 528 : heap_freetuple(tup);
667 : :
668 : 528 : ObjectAddressSet(myself, PropgraphElementRelationId, peoid);
669 : :
670 : : /* Add dependency on the property graph */
671 : 528 : recordDependencyOn(&myself, &pgaddress, DEPENDENCY_AUTO);
672 : :
673 : 528 : addrs = new_object_addresses();
674 : :
675 : : /* Add dependency on the relation */
676 : 528 : ObjectAddressSet(referenced, RelationRelationId, einfo->relid);
677 : 528 : add_exact_object_address(&referenced, addrs);
678 : 528 : 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 [ + + ]: 528 : if (einfo->srcvertexid)
685 : : {
686 : 197 : ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->srcvertexid);
687 : 197 : add_exact_object_address(&referenced, addrs);
688 : 197 : array_of_attnums_to_objectaddrs(einfo->relid, einfo->srckey, addrs);
689 : 197 : array_of_attnums_to_objectaddrs(einfo->srcrelid, einfo->srcref, addrs);
690 : 197 : array_of_opers_to_objectaddrs(einfo->srceqop, addrs);
691 : : }
692 [ + + ]: 528 : if (einfo->destvertexid)
693 : : {
694 : 197 : ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->destvertexid);
695 : 197 : add_exact_object_address(&referenced, addrs);
696 : 197 : array_of_attnums_to_objectaddrs(einfo->relid, einfo->destkey, addrs);
697 : 197 : array_of_attnums_to_objectaddrs(einfo->destrelid, einfo->destref, addrs);
698 : 197 : array_of_opers_to_objectaddrs(einfo->desteqop, addrs);
699 : : }
700 : :
701 : 528 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
702 : :
703 : 528 : table_close(rel, NoLock);
704 : :
705 [ + - ]: 528 : if (einfo->labels)
706 : : {
707 : : ListCell *lc;
708 : :
709 [ + - + + : 1143 : foreach(lc, einfo->labels)
+ + ]
710 : : {
711 : 643 : PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
712 : : Oid ellabeloid;
713 : :
714 [ + + ]: 643 : if (lp->label)
715 : 272 : ellabeloid = insert_label_record(graphid, peoid, lp->label);
716 : : else
717 : 371 : ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname);
718 : 643 : insert_property_records(graphid, ellabeloid, einfo->relid, lp->properties);
719 : :
720 : 615 : 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 : 500 : 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 : 675 : 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 : 675 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(graphid), CStringGetDatum(label));
754 [ + + ]: 675 : if (!labeloid)
755 : : {
756 : : Relation rel;
757 : 526 : Datum values[Natts_pg_propgraph_label] = {0};
758 : 526 : bool nulls[Natts_pg_propgraph_label] = {0};
759 : : NameData labelname;
760 : : HeapTuple tup;
761 : : ObjectAddress myself;
762 : : ObjectAddress referenced;
763 : :
764 : 526 : rel = table_open(PropgraphLabelRelationId, RowExclusiveLock);
765 : :
766 : 526 : labeloid = GetNewOidWithIndex(rel, PropgraphLabelObjectIndexId, Anum_pg_propgraph_label_oid);
767 : 526 : values[Anum_pg_propgraph_label_oid - 1] = ObjectIdGetDatum(labeloid);
768 : 526 : values[Anum_pg_propgraph_label_pglpgid - 1] = ObjectIdGetDatum(graphid);
769 : 526 : namestrcpy(&labelname, label);
770 : 526 : values[Anum_pg_propgraph_label_pgllabel - 1] = NameGetDatum(&labelname);
771 : :
772 : 526 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
773 : 526 : CatalogTupleInsert(rel, tup);
774 : 526 : heap_freetuple(tup);
775 : :
776 : 526 : ObjectAddressSet(myself, PropgraphLabelRelationId, labeloid);
777 : :
778 : 526 : ObjectAddressSet(referenced, RelationRelationId, graphid);
779 : 526 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
780 : :
781 : 526 : table_close(rel, NoLock);
782 : : }
783 : :
784 : : /*
785 : : * Insert into pg_propgraph_element_label
786 : : */
787 : : {
788 : : Relation rel;
789 : 675 : Datum values[Natts_pg_propgraph_element_label] = {0};
790 : 675 : bool nulls[Natts_pg_propgraph_element_label] = {0};
791 : : HeapTuple tup;
792 : : ObjectAddress myself;
793 : : ObjectAddress referenced;
794 : :
795 : 675 : rel = table_open(PropgraphElementLabelRelationId, RowExclusiveLock);
796 : :
797 : 675 : ellabeloid = GetNewOidWithIndex(rel, PropgraphElementLabelObjectIndexId, Anum_pg_propgraph_element_label_oid);
798 : 675 : values[Anum_pg_propgraph_element_label_oid - 1] = ObjectIdGetDatum(ellabeloid);
799 : 675 : values[Anum_pg_propgraph_element_label_pgellabelid - 1] = ObjectIdGetDatum(labeloid);
800 : 675 : values[Anum_pg_propgraph_element_label_pgelelid - 1] = ObjectIdGetDatum(peoid);
801 : :
802 : 675 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
803 : 675 : CatalogTupleInsert(rel, tup);
804 : 675 : heap_freetuple(tup);
805 : :
806 : 675 : ObjectAddressSet(myself, PropgraphElementLabelRelationId, ellabeloid);
807 : :
808 : 675 : ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid);
809 : 675 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
810 : 675 : ObjectAddressSet(referenced, PropgraphElementRelationId, peoid);
811 : 675 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
812 : :
813 : 675 : table_close(rel, NoLock);
814 : : }
815 : :
816 : 675 : return ellabeloid;
817 : : }
818 : :
819 : : /*
820 : : * Insert records for properties into the pg_propgraph_property catalog.
821 : : */
822 : : static void
823 : 683 : insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties)
824 : : {
825 : 683 : List *proplist = NIL;
826 : : ParseState *pstate;
827 : : ParseNamespaceItem *nsitem;
828 : : List *tp;
829 : : Relation rel;
830 : : ListCell *lc;
831 : :
832 [ + + ]: 683 : if (properties->all)
833 : : {
834 : : Relation attRelation;
835 : : SysScanDesc scan;
836 : : ScanKeyData key[1];
837 : : HeapTuple attributeTuple;
838 : :
839 : 290 : attRelation = table_open(AttributeRelationId, RowShareLock);
840 : 290 : ScanKeyInit(&key[0],
841 : : Anum_pg_attribute_attrelid,
842 : : BTEqualStrategyNumber, F_OIDEQ,
843 : : ObjectIdGetDatum(pgerelid));
844 : 290 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
845 : : true, NULL, 1, key);
846 [ + + ]: 2806 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
847 : : {
848 : 2516 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
849 : : ColumnRef *cr;
850 : : ResTarget *rt;
851 : :
852 [ + + + + ]: 2516 : if (att->attnum <= 0 || att->attisdropped)
853 : 1728 : continue;
854 : :
855 : 788 : cr = makeNode(ColumnRef);
856 : 788 : rt = makeNode(ResTarget);
857 : :
858 : 788 : cr->fields = list_make1(makeString(pstrdup(NameStr(att->attname))));
859 : 788 : cr->location = -1;
860 : :
861 : 788 : rt->name = pstrdup(NameStr(att->attname));
862 : 788 : rt->val = (Node *) cr;
863 : 788 : rt->location = -1;
864 : :
865 : 788 : proplist = lappend(proplist, rt);
866 : : }
867 : 290 : systable_endscan(scan);
868 : 290 : table_close(attRelation, RowShareLock);
869 : : }
870 : : else
871 : : {
872 : 393 : proplist = properties->properties;
873 : :
874 [ + + + + : 1058 : foreach(lc, proplist)
+ + ]
875 : : {
876 : 665 : ResTarget *rt = lfirst_node(ResTarget, lc);
877 : :
878 [ + + - + ]: 665 : 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 : 683 : rel = table_open(pgerelid, AccessShareLock);
887 : :
888 : 683 : pstate = make_parsestate(NULL);
889 : 683 : nsitem = addRangeTableEntryForRelation(pstate,
890 : : rel,
891 : : AccessShareLock,
892 : : NULL,
893 : : false,
894 : : true);
895 : 683 : addNSItemToQuery(pstate, nsitem, true, true, true);
896 : :
897 : 683 : table_close(rel, NoLock);
898 : :
899 : 683 : tp = transformTargetList(pstate, proplist, EXPR_KIND_PROPGRAPH_PROPERTY);
900 [ + - ]: 683 : if (pstate->p_resolve_unknowns)
901 : 683 : resolveTargetListUnknowns(pstate, tp);
902 : 683 : assign_expr_collations(pstate, (Node *) tp);
903 : :
904 [ + + + + : 2104 : foreach(lc, tp)
+ + ]
905 : : {
906 : 1453 : TargetEntry *te = lfirst_node(TargetEntry, lc);
907 : :
908 : 1453 : insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr);
909 : : }
910 : 651 : }
911 : :
912 : : /*
913 : : * Insert records for a property into the pg_propgraph_property and
914 : : * pg_propgraph_label_property catalogs, and register dependencies.
915 : : */
916 : : static void
917 : 1453 : insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr)
918 : : {
919 : : Oid propoid;
920 : 1453 : Oid exprtypid = exprType((const Node *) expr);
921 : 1453 : int32 exprtypmod = exprTypmod((const Node *) expr);
922 : 1453 : Oid exprcollation = exprCollation((const Node *) expr);
923 : :
924 : : /*
925 : : * Insert into pg_propgraph_property if not already existing.
926 : : */
927 : 1453 : propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, Anum_pg_propgraph_property_oid, ObjectIdGetDatum(graphid), CStringGetDatum(propname));
928 [ + + ]: 1453 : if (!OidIsValid(propoid))
929 : : {
930 : : Relation rel;
931 : : NameData propnamedata;
932 : 799 : Datum values[Natts_pg_propgraph_property] = {0};
933 : 799 : bool nulls[Natts_pg_propgraph_property] = {0};
934 : : HeapTuple tup;
935 : : ObjectAddress myself;
936 : : ObjectAddress referenced;
937 : :
938 : 799 : rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock);
939 : :
940 : 799 : propoid = GetNewOidWithIndex(rel, PropgraphPropertyObjectIndexId, Anum_pg_propgraph_property_oid);
941 : 799 : values[Anum_pg_propgraph_property_oid - 1] = ObjectIdGetDatum(propoid);
942 : 799 : values[Anum_pg_propgraph_property_pgppgid - 1] = ObjectIdGetDatum(graphid);
943 : 799 : namestrcpy(&propnamedata, propname);
944 : 799 : values[Anum_pg_propgraph_property_pgpname - 1] = NameGetDatum(&propnamedata);
945 : 799 : values[Anum_pg_propgraph_property_pgptypid - 1] = ObjectIdGetDatum(exprtypid);
946 : 799 : values[Anum_pg_propgraph_property_pgptypmod - 1] = Int32GetDatum(exprtypmod);
947 : 799 : values[Anum_pg_propgraph_property_pgpcollation - 1] = ObjectIdGetDatum(exprcollation);
948 : :
949 : 799 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
950 : 799 : CatalogTupleInsert(rel, tup);
951 : 799 : heap_freetuple(tup);
952 : :
953 : 799 : ObjectAddressSet(myself, PropgraphPropertyRelationId, propoid);
954 : :
955 : 799 : ObjectAddressSet(referenced, RelationRelationId, graphid);
956 : 799 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
957 : 799 : ObjectAddressSet(referenced, TypeRelationId, exprtypid);
958 : 799 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
959 [ + + + + ]: 799 : if (OidIsValid(exprcollation) && exprcollation != DEFAULT_COLLATION_OID)
960 : : {
961 : 23 : ObjectAddressSet(referenced, CollationRelationId, exprcollation);
962 : 23 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
963 : : }
964 : :
965 : 799 : table_close(rel, NoLock);
966 : : }
967 : : else
968 : : {
969 : 654 : HeapTuple pgptup = SearchSysCache1(PROPGRAPHPROPOID, ObjectIdGetDatum(propoid));
970 : 654 : Form_pg_propgraph_property pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup);
971 : 654 : Oid proptypid = pgpform->pgptypid;
972 : 654 : int32 proptypmod = pgpform->pgptypmod;
973 : 654 : Oid propcollation = pgpform->pgpcollation;
974 : :
975 : 654 : ReleaseSysCache(pgptup);
976 : :
977 : : /*
978 : : * Check that in the graph, all properties with the same name have the
979 : : * same type (independent of which label they are on). (See SQL/PGQ
980 : : * subclause "Consistency check of a tabular property graph
981 : : * descriptor".)
982 : : */
983 [ + + + + ]: 654 : if (proptypid != exprtypid || proptypmod != exprtypmod)
984 : : {
985 [ + - ]: 16 : ereport(ERROR,
986 : : errcode(ERRCODE_SYNTAX_ERROR),
987 : : errmsg("property \"%s\" data type mismatch: %s vs. %s",
988 : : propname, format_type_with_typemod(proptypid, proptypmod), format_type_with_typemod(exprtypid, exprtypmod)),
989 : : errdetail("In a property graph, a property of the same name has to have the same data type in each label."));
990 : : }
991 : :
992 : : /* Similarly for collation */
993 [ + + ]: 638 : if (propcollation != exprcollation)
994 : : {
995 [ + - ]: 16 : ereport(ERROR,
996 : : errcode(ERRCODE_SYNTAX_ERROR),
997 : : errmsg("property \"%s\" collation mismatch: %s vs. %s",
998 : : propname, get_collation_name(propcollation), get_collation_name(exprcollation)),
999 : : errdetail("In a property graph, a property of the same name has to have the same collation in each label."));
1000 : : }
1001 : : }
1002 : :
1003 : : /*
1004 : : * Insert into pg_propgraph_label_property
1005 : : */
1006 : : {
1007 : : Relation rel;
1008 : 1421 : Datum values[Natts_pg_propgraph_label_property] = {0};
1009 : 1421 : bool nulls[Natts_pg_propgraph_label_property] = {0};
1010 : : Oid plpoid;
1011 : : HeapTuple tup;
1012 : : ObjectAddress myself;
1013 : : ObjectAddress referenced;
1014 : :
1015 : 1421 : rel = table_open(PropgraphLabelPropertyRelationId, RowExclusiveLock);
1016 : :
1017 : 1421 : plpoid = GetNewOidWithIndex(rel, PropgraphLabelPropertyObjectIndexId, Anum_pg_propgraph_label_property_oid);
1018 : 1421 : values[Anum_pg_propgraph_label_property_oid - 1] = ObjectIdGetDatum(plpoid);
1019 : 1421 : values[Anum_pg_propgraph_label_property_plppropid - 1] = ObjectIdGetDatum(propoid);
1020 : 1421 : values[Anum_pg_propgraph_label_property_plpellabelid - 1] = ObjectIdGetDatum(ellabeloid);
1021 : 1421 : values[Anum_pg_propgraph_label_property_plpexpr - 1] = CStringGetTextDatum(nodeToString(expr));
1022 : :
1023 : 1421 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
1024 : 1421 : CatalogTupleInsert(rel, tup);
1025 : 1421 : heap_freetuple(tup);
1026 : :
1027 : 1421 : ObjectAddressSet(myself, PropgraphLabelPropertyRelationId, plpoid);
1028 : :
1029 : 1421 : ObjectAddressSet(referenced, PropgraphPropertyRelationId, propoid);
1030 : 1421 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1031 : :
1032 : 1421 : ObjectAddressSet(referenced, PropgraphElementLabelRelationId, ellabeloid);
1033 : 1421 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1034 : :
1035 : 1421 : recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false);
1036 : :
1037 : 1421 : table_close(rel, NoLock);
1038 : : }
1039 : 1421 : }
1040 : :
1041 : : /*
1042 : : * Check that for the given graph element, all properties with the same name
1043 : : * have the same expression for each label. (See SQL/PGQ subclause "Creation
1044 : : * of an element table descriptor".)
1045 : : *
1046 : : * We check this after all the catalog records are already inserted. This
1047 : : * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER
1048 : : * PROPERTY GRAPH. We pass in the element OID so that ALTER PROPERTY GRAPH
1049 : : * only has to check the element it has just operated on. CREATE PROPERTY
1050 : : * GRAPH checks all elements it has created.
1051 : : */
1052 : : static void
1053 : 504 : check_element_properties(Oid peoid)
1054 : : {
1055 : : Relation rel1;
1056 : : ScanKeyData key1[1];
1057 : : SysScanDesc scan1;
1058 : : HeapTuple tuple1;
1059 : 504 : List *propoids = NIL;
1060 : 504 : List *propexprs = NIL;
1061 : :
1062 : 504 : rel1 = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1063 : 504 : ScanKeyInit(&key1[0],
1064 : : Anum_pg_propgraph_element_label_pgelelid,
1065 : : BTEqualStrategyNumber, F_OIDEQ,
1066 : : ObjectIdGetDatum(peoid));
1067 : :
1068 : 504 : scan1 = systable_beginscan(rel1, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, key1);
1069 [ + + ]: 1147 : while (HeapTupleIsValid(tuple1 = systable_getnext(scan1)))
1070 : : {
1071 : 651 : Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple1);
1072 : : Relation rel2;
1073 : : ScanKeyData key2[1];
1074 : : SysScanDesc scan2;
1075 : : HeapTuple tuple2;
1076 : :
1077 : 651 : rel2 = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
1078 : 651 : ScanKeyInit(&key2[0],
1079 : : Anum_pg_propgraph_label_property_plpellabelid,
1080 : : BTEqualStrategyNumber, F_OIDEQ,
1081 : : ObjectIdGetDatum(ellabel->oid));
1082 : :
1083 : 651 : scan2 = systable_beginscan(rel2, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key2);
1084 [ + + ]: 2052 : while (HeapTupleIsValid(tuple2 = systable_getnext(scan2)))
1085 : : {
1086 : 1409 : Form_pg_propgraph_label_property lprop = (Form_pg_propgraph_label_property) GETSTRUCT(tuple2);
1087 : : Oid propoid;
1088 : : Datum datum;
1089 : : bool isnull;
1090 : : char *propexpr;
1091 : : ListCell *lc1,
1092 : : *lc2;
1093 : : bool found;
1094 : :
1095 : 1409 : propoid = lprop->plppropid;
1096 : 1409 : datum = heap_getattr(tuple2, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(rel2), &isnull);
1097 : : Assert(!isnull);
1098 : 1409 : propexpr = TextDatumGetCString(datum);
1099 : :
1100 : 1409 : found = false;
1101 [ + + + + : 2794 : forboth(lc1, propoids, lc2, propexprs)
+ + + + +
+ + - +
+ ]
1102 : : {
1103 [ + + ]: 1499 : if (propoid == lfirst_oid(lc1))
1104 : : {
1105 : : Node *na,
1106 : : *nb;
1107 : :
1108 : 114 : na = stringToNode(propexpr);
1109 : 114 : nb = stringToNode(lfirst(lc2));
1110 : :
1111 : 114 : found = true;
1112 : :
1113 [ + + ]: 114 : if (!equal(na, nb))
1114 : : {
1115 : : HeapTuple tuple3;
1116 : : Form_pg_propgraph_element elform;
1117 : : List *dpcontext;
1118 : : char *dpa,
1119 : : *dpb;
1120 : :
1121 : 8 : tuple3 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peoid));
1122 [ - + ]: 8 : if (!tuple3)
1123 [ # # ]: 0 : elog(ERROR, "cache lookup failed for property graph element %u", peoid);
1124 : 8 : elform = (Form_pg_propgraph_element) GETSTRUCT(tuple3);
1125 : 8 : dpcontext = deparse_context_for(get_rel_name(elform->pgerelid), elform->pgerelid);
1126 : :
1127 : 8 : dpa = deparse_expression(na, dpcontext, false, false);
1128 : 8 : dpb = deparse_expression(nb, dpcontext, false, false);
1129 : :
1130 : : /*
1131 : : * show in sorted order to keep output independent of
1132 : : * index order
1133 : : */
1134 [ - + ]: 8 : if (strcmp(dpa, dpb) > 0)
1135 : : {
1136 : : char *tmp;
1137 : :
1138 : 0 : tmp = dpa;
1139 : 0 : dpa = dpb;
1140 : 0 : dpb = tmp;
1141 : : }
1142 : :
1143 [ + - ]: 8 : ereport(ERROR,
1144 : : errcode(ERRCODE_SYNTAX_ERROR),
1145 : : errmsg("element \"%s\" property \"%s\" expression mismatch: %s vs. %s",
1146 : : NameStr(elform->pgealias), get_propgraph_property_name(propoid), dpa, dpb),
1147 : : errdetail("In a property graph element, a property of the same name has to have the same expression in each label."));
1148 : :
1149 : : ReleaseSysCache(tuple3);
1150 : : }
1151 : :
1152 : 106 : break;
1153 : : }
1154 : : }
1155 : :
1156 [ + + ]: 1401 : if (!found)
1157 : : {
1158 : 1295 : propoids = lappend_oid(propoids, propoid);
1159 : 1295 : propexprs = lappend(propexprs, propexpr);
1160 : : }
1161 : : }
1162 : 643 : systable_endscan(scan2);
1163 : 643 : table_close(rel2, AccessShareLock);
1164 : : }
1165 : :
1166 : 496 : systable_endscan(scan1);
1167 : 496 : table_close(rel1, AccessShareLock);
1168 : 496 : }
1169 : :
1170 : : /*
1171 : : * Check that for the given element label, all labels of the same name in the
1172 : : * graph have the same number and names of properties (independent of which
1173 : : * element they are on). (See SQL/PGQ subclause "Consistency check of a
1174 : : * tabular property graph descriptor".)
1175 : : *
1176 : : * We check this after all the catalog records are already inserted. This
1177 : : * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER
1178 : : * PROPERTY GRAPH. We pass in the element label OID so that some variants of
1179 : : * ALTER PROPERTY GRAPH only have to check the element label it has just
1180 : : * operated on. CREATE PROPERTY GRAPH and other ALTER PROPERTY GRAPH variants
1181 : : * check all labels.
1182 : : */
1183 : : static void
1184 : 987 : check_element_label_properties(Oid ellabeloid)
1185 : : {
1186 : : Relation rel;
1187 : : SysScanDesc scan;
1188 : : ScanKeyData key[1];
1189 : : HeapTuple tuple;
1190 : 987 : Oid labelid = InvalidOid;
1191 : 987 : Oid ref_ellabeloid = InvalidOid;
1192 : : List *myprops,
1193 : : *refprops;
1194 : : List *diff1,
1195 : : *diff2;
1196 : :
1197 : 987 : rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1198 : :
1199 : : /*
1200 : : * Get element label info
1201 : : */
1202 : 987 : ScanKeyInit(&key[0],
1203 : : Anum_pg_propgraph_element_label_oid,
1204 : : BTEqualStrategyNumber,
1205 : : F_OIDEQ, ObjectIdGetDatum(ellabeloid));
1206 : 987 : scan = systable_beginscan(rel, PropgraphElementLabelObjectIndexId, true, NULL, 1, key);
1207 [ + - ]: 987 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1208 : : {
1209 : 987 : Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
1210 : :
1211 : 987 : labelid = ellabel->pgellabelid;
1212 : : }
1213 : 987 : systable_endscan(scan);
1214 [ - + ]: 987 : if (!labelid)
1215 [ # # ]: 0 : elog(ERROR, "element label %u not found", ellabeloid);
1216 : :
1217 : : /*
1218 : : * Find a reference element label to fetch label properties. The
1219 : : * reference element label has to have the same label OID as the one being
1220 : : * checked but a different element OID.
1221 : : */
1222 : 987 : ScanKeyInit(&key[0],
1223 : : Anum_pg_propgraph_element_label_pgellabelid,
1224 : : BTEqualStrategyNumber,
1225 : : F_OIDEQ, ObjectIdGetDatum(labelid));
1226 : 987 : scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
1227 [ + + ]: 1649 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1228 : : {
1229 : 1116 : Form_pg_propgraph_element_label otherellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
1230 : :
1231 [ + + ]: 1116 : if (otherellabel->oid != ellabeloid)
1232 : : {
1233 : 454 : ref_ellabeloid = otherellabel->oid;
1234 : 454 : break;
1235 : : }
1236 : : }
1237 : 987 : systable_endscan(scan);
1238 : :
1239 : 987 : table_close(rel, AccessShareLock);
1240 : :
1241 : : /*
1242 : : * If there is no previous definition of this label, then we are done.
1243 : : */
1244 [ + + ]: 987 : if (!ref_ellabeloid)
1245 : 533 : return;
1246 : :
1247 : : /*
1248 : : * Now check number and names.
1249 : : *
1250 : : * XXX We could provide more detail in the error messages, but that would
1251 : : * probably only be useful for some ALTER commands, because otherwise it's
1252 : : * not really clear which label definition is the wrong one, and so you'd
1253 : : * have to construct a rather verbose report to be of any use. Let's keep
1254 : : * it simple for now.
1255 : : */
1256 : :
1257 : 454 : myprops = get_element_label_property_names(ellabeloid);
1258 : 454 : refprops = get_element_label_property_names(ref_ellabeloid);
1259 : :
1260 [ + + ]: 454 : if (list_length(refprops) != list_length(myprops))
1261 [ + - ]: 16 : ereport(ERROR,
1262 : : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1263 : : errmsg("mismatching number of properties in definition of label \"%s\"", get_propgraph_label_name(labelid)));
1264 : :
1265 : 438 : diff1 = list_difference(myprops, refprops);
1266 : 438 : diff2 = list_difference(refprops, myprops);
1267 : :
1268 [ + + - + ]: 438 : if (diff1 || diff2)
1269 [ + - ]: 8 : ereport(ERROR,
1270 : : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1271 : : errmsg("mismatching property names in definition of label \"%s\"", get_propgraph_label_name(labelid)));
1272 : : }
1273 : :
1274 : : /*
1275 : : * As above, but check all labels of a graph.
1276 : : */
1277 : : static void
1278 : 198 : check_all_labels_properties(Oid pgrelid)
1279 : : {
1280 [ + + + + : 994 : foreach_oid(labeloid, get_graph_label_ids(pgrelid))
+ + ]
1281 : : {
1282 [ + - + + : 2151 : foreach_oid(ellabeloid, get_label_element_label_ids(labeloid))
+ + ]
1283 : : {
1284 : 931 : check_element_label_properties(ellabeloid);
1285 : : }
1286 : : }
1287 : 186 : }
1288 : :
1289 : : /*
1290 : : * ALTER PROPERTY GRAPH
1291 : : */
1292 : : ObjectAddress
1293 : 160 : AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt)
1294 : : {
1295 : : Oid pgrelid;
1296 : : ListCell *lc;
1297 : : ObjectAddress pgaddress;
1298 : :
1299 : : /*
1300 : : * ShareRowExclusiveLock is required because this command runs some
1301 : : * graph-wide consistency checks that wouldn't work if more than one ALTER
1302 : : * PROPERTY GRAPH could operate on the same graph at once.
1303 : : */
1304 : 160 : pgrelid = RangeVarGetRelidExtended(stmt->pgname,
1305 : : ShareRowExclusiveLock,
1306 : 160 : stmt->missing_ok ? RVR_MISSING_OK : 0,
1307 : : RangeVarCallbackOwnsRelation,
1308 : : NULL);
1309 [ - + ]: 156 : if (pgrelid == InvalidOid)
1310 : : {
1311 [ # # ]: 0 : ereport(NOTICE,
1312 : : (errmsg("relation \"%s\" does not exist, skipping",
1313 : : stmt->pgname->relname)));
1314 : 0 : return InvalidObjectAddress;
1315 : : }
1316 : :
1317 : 156 : ObjectAddressSet(pgaddress, RelationRelationId, pgrelid);
1318 : :
1319 [ + + + + : 172 : foreach(lc, stmt->add_vertex_tables)
+ + ]
1320 : : {
1321 : 32 : PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
1322 : : struct element_info *vinfo;
1323 : : Relation rel;
1324 : : Oid peoid;
1325 : :
1326 : 32 : vinfo = palloc0_object(struct element_info);
1327 : 32 : vinfo->kind = PGEKIND_VERTEX;
1328 : :
1329 : 32 : vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
1330 : :
1331 : 32 : rel = table_open(vinfo->relid, NoLock);
1332 : :
1333 [ + + + - ]: 32 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP)
1334 [ + - ]: 4 : ereport(ERROR,
1335 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1336 : : errmsg("cannot add temporary element table to non-temporary property graph"),
1337 : : errdetail("Table \"%s\" is a temporary table.", get_rel_name(vinfo->relid)),
1338 : : parser_errposition(pstate, vertex->vtable->location)));
1339 : :
1340 [ + + ]: 28 : if (vertex->vtable->alias)
1341 : 4 : vinfo->aliasname = vertex->vtable->alias->aliasname;
1342 : : else
1343 : 24 : vinfo->aliasname = vertex->vtable->relname;
1344 : :
1345 : 28 : vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location);
1346 : :
1347 : 28 : vinfo->labels = vertex->labels;
1348 : :
1349 : 28 : table_close(rel, NoLock);
1350 : :
1351 [ + + ]: 28 : if (SearchSysCacheExists2(PROPGRAPHELALIAS,
1352 : : ObjectIdGetDatum(pgrelid),
1353 : : CStringGetDatum(vinfo->aliasname)))
1354 [ + - ]: 4 : ereport(ERROR,
1355 : : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1356 : : errmsg("alias \"%s\" already exists in property graph \"%s\"",
1357 : : vinfo->aliasname, stmt->pgname->relname),
1358 : : parser_errposition(pstate, vertex->vtable->location));
1359 : :
1360 : 24 : peoid = insert_element_record(pgaddress, vinfo);
1361 : :
1362 : 20 : CommandCounterIncrement();
1363 : 20 : check_element_properties(peoid);
1364 : 16 : check_all_labels_properties(pgrelid);
1365 : : }
1366 : :
1367 [ + + + + : 172 : foreach(lc, stmt->add_edge_tables)
+ + ]
1368 : : {
1369 : 36 : PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc);
1370 : : struct element_info *einfo;
1371 : : Relation rel;
1372 : : Relation srcrel;
1373 : : Relation destrel;
1374 : : Oid peoid;
1375 : :
1376 : 36 : einfo = palloc0_object(struct element_info);
1377 : 36 : einfo->kind = PGEKIND_EDGE;
1378 : :
1379 : 36 : einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
1380 : :
1381 : 36 : rel = table_open(einfo->relid, NoLock);
1382 : :
1383 [ - + - - ]: 36 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP)
1384 [ # # ]: 0 : ereport(ERROR,
1385 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1386 : : errmsg("cannot add temporary element table to non-temporary property graph"),
1387 : : errdetail("Table \"%s\" is a temporary table.", get_rel_name(einfo->relid)),
1388 : : parser_errposition(pstate, edge->etable->location)));
1389 : :
1390 [ - + ]: 36 : if (edge->etable->alias)
1391 : 0 : einfo->aliasname = edge->etable->alias->aliasname;
1392 : : else
1393 : 36 : einfo->aliasname = edge->etable->relname;
1394 : :
1395 : 36 : einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location);
1396 : :
1397 : 36 : einfo->srcvertexid = get_vertex_oid(pstate, pgrelid, edge->esrcvertex, edge->location);
1398 : 36 : einfo->destvertexid = get_vertex_oid(pstate, pgrelid, edge->edestvertex, edge->location);
1399 : :
1400 : 36 : einfo->srcrelid = get_element_relid(einfo->srcvertexid);
1401 : 36 : einfo->destrelid = get_element_relid(einfo->destvertexid);
1402 : :
1403 : 36 : srcrel = table_open(einfo->srcrelid, AccessShareLock);
1404 : 36 : destrel = table_open(einfo->destrelid, AccessShareLock);
1405 : :
1406 : 36 : propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel,
1407 : 36 : einfo->aliasname, edge->location, "SOURCE",
1408 : : &einfo->srckey, &einfo->srcref, &einfo->srceqop);
1409 : 36 : propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel,
1410 : 36 : einfo->aliasname, edge->location, "DESTINATION",
1411 : : &einfo->destkey, &einfo->destref, &einfo->desteqop);
1412 : :
1413 : 36 : einfo->labels = edge->labels;
1414 : :
1415 : 36 : table_close(destrel, NoLock);
1416 : 36 : table_close(srcrel, NoLock);
1417 : :
1418 : 36 : table_close(rel, NoLock);
1419 : :
1420 [ - + ]: 36 : if (SearchSysCacheExists2(PROPGRAPHELALIAS,
1421 : : ObjectIdGetDatum(pgrelid),
1422 : : CStringGetDatum(einfo->aliasname)))
1423 [ # # ]: 0 : ereport(ERROR,
1424 : : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1425 : : errmsg("alias \"%s\" already exists in property graph \"%s\"",
1426 : : einfo->aliasname, stmt->pgname->relname),
1427 : : parser_errposition(pstate, edge->etable->location));
1428 : :
1429 : 36 : peoid = insert_element_record(pgaddress, einfo);
1430 : :
1431 : 32 : CommandCounterIncrement();
1432 : 32 : check_element_properties(peoid);
1433 : 32 : check_all_labels_properties(pgrelid);
1434 : : }
1435 : :
1436 [ + + + + : 140 : foreach(lc, stmt->drop_vertex_tables)
+ + ]
1437 : : {
1438 : 8 : char *alias = strVal(lfirst(lc));
1439 : : Oid peoid;
1440 : : ObjectAddress obj;
1441 : :
1442 : 8 : peoid = get_vertex_oid(pstate, pgrelid, alias, -1);
1443 : 8 : ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
1444 : 8 : performDeletion(&obj, stmt->drop_behavior, 0);
1445 : : }
1446 : :
1447 [ + + + + : 144 : foreach(lc, stmt->drop_edge_tables)
+ + ]
1448 : : {
1449 : 12 : char *alias = strVal(lfirst(lc));
1450 : : Oid peoid;
1451 : : ObjectAddress obj;
1452 : :
1453 : 12 : peoid = get_edge_oid(pstate, pgrelid, alias, -1);
1454 : 12 : ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
1455 : 12 : performDeletion(&obj, stmt->drop_behavior, 0);
1456 : : }
1457 : :
1458 : : /* Remove any orphaned pg_propgraph_label entries */
1459 [ + + + + ]: 132 : if (stmt->drop_vertex_tables || stmt->drop_edge_tables)
1460 : : {
1461 [ + - + + : 88 : foreach_oid(labeloid, get_graph_label_ids(pgrelid))
+ + ]
1462 : : {
1463 [ + + ]: 64 : if (!get_label_element_label_ids(labeloid))
1464 : : {
1465 : : ObjectAddress obj;
1466 : :
1467 : 12 : ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid);
1468 : 12 : performDeletion(&obj, stmt->drop_behavior, 0);
1469 : : }
1470 : : }
1471 : : }
1472 : :
1473 [ + + + + : 148 : foreach(lc, stmt->add_labels)
+ + ]
1474 : : {
1475 : 32 : PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
1476 : : Oid peoid;
1477 : : Oid pgerelid;
1478 : : Oid ellabeloid;
1479 : :
1480 : : Assert(lp->label);
1481 : :
1482 [ + - ]: 32 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1483 : 32 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1484 : : else
1485 : 0 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1486 : :
1487 : 32 : pgerelid = get_element_relid(peoid);
1488 : :
1489 : 32 : ellabeloid = insert_label_record(pgrelid, peoid, lp->label);
1490 : 32 : insert_property_records(pgrelid, ellabeloid, pgerelid, lp->properties);
1491 : :
1492 : 28 : CommandCounterIncrement();
1493 : 28 : check_element_properties(peoid);
1494 : 28 : check_element_label_properties(ellabeloid);
1495 : : }
1496 : :
1497 [ + + ]: 116 : if (stmt->drop_label)
1498 : : {
1499 : : Oid peoid;
1500 : : Oid labeloid;
1501 : 28 : Oid ellabeloid = InvalidOid;
1502 : : ObjectAddress obj;
1503 : : Relation ellabelrel;
1504 : : SysScanDesc ellabelscan;
1505 : : ScanKeyData ellabelkey[1];
1506 : : int nlabels;
1507 : : HeapTuple tuple;
1508 : :
1509 [ + - ]: 28 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1510 : 28 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1511 : : else
1512 : 0 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1513 : :
1514 : 28 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
1515 : : Anum_pg_propgraph_label_oid,
1516 : : ObjectIdGetDatum(pgrelid),
1517 : : CStringGetDatum(stmt->drop_label));
1518 [ + + ]: 28 : if (!labeloid)
1519 [ + - ]: 4 : 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 : : /*
1526 : : * Is the given label associated with the element? Is this the only
1527 : : * label associated with the element? Scan the
1528 : : * pg_propgraph_element_label table to find answers to these
1529 : : * questions. Stop scanning when we know both answers.
1530 : : */
1531 : 24 : ellabelrel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1532 : 24 : ScanKeyInit(&ellabelkey[0],
1533 : : Anum_pg_propgraph_element_label_pgelelid,
1534 : : BTEqualStrategyNumber, F_OIDEQ,
1535 : : ObjectIdGetDatum(peoid));
1536 : 24 : ellabelscan = systable_beginscan(ellabelrel, PropgraphElementLabelElementLabelIndexId,
1537 : : true, NULL, 1, ellabelkey);
1538 : 24 : nlabels = 0;
1539 [ + + ]: 52 : while (HeapTupleIsValid(tuple = systable_getnext(ellabelscan)))
1540 : : {
1541 : 48 : Form_pg_propgraph_element_label ellabelform = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
1542 : :
1543 : 48 : nlabels++;
1544 : :
1545 [ + + ]: 48 : if (ellabelform->pgellabelid == labeloid)
1546 : 24 : ellabeloid = ellabelform->oid;
1547 : :
1548 [ + + + + ]: 48 : if (nlabels > 1 && ellabeloid)
1549 : 20 : break;
1550 : : }
1551 : 24 : systable_endscan(ellabelscan);
1552 : 24 : table_close(ellabelrel, AccessShareLock);
1553 : :
1554 [ - + ]: 24 : if (!ellabeloid)
1555 [ # # ]: 0 : ereport(ERROR,
1556 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1557 : : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1558 : : get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
1559 : : parser_errposition(pstate, -1));
1560 : :
1561 : : /*
1562 : : * Prevent dropping the last label from an element. Every element must
1563 : : * have at least one label associated with it.
1564 : : */
1565 [ + + ]: 24 : if (nlabels == 1)
1566 [ + - ]: 4 : ereport(ERROR,
1567 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1568 : : errmsg("cannot drop the last label from element \"%s\"",
1569 : : stmt->element_alias),
1570 : : errhint("Every element must have at least one label.")));
1571 : :
1572 : 20 : ObjectAddressSet(obj, PropgraphElementLabelRelationId, ellabeloid);
1573 : 20 : performDeletion(&obj, stmt->drop_behavior, 0);
1574 : :
1575 : : /* Remove any orphaned pg_propgraph_label entries */
1576 [ + + ]: 20 : if (!get_label_element_label_ids(labeloid))
1577 : : {
1578 : 16 : ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid);
1579 : 16 : performDeletion(&obj, stmt->drop_behavior, 0);
1580 : : }
1581 : : }
1582 : :
1583 [ + + ]: 104 : if (stmt->add_properties)
1584 : : {
1585 : : Oid peoid;
1586 : : Oid pgerelid;
1587 : : Oid labeloid;
1588 : 8 : Oid ellabeloid = InvalidOid;
1589 : :
1590 [ + + ]: 8 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1591 : 4 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1592 : : else
1593 : 4 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1594 : :
1595 : 8 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
1596 : : Anum_pg_propgraph_label_oid,
1597 : : ObjectIdGetDatum(pgrelid),
1598 : : CStringGetDatum(stmt->alter_label));
1599 [ + - ]: 8 : if (labeloid)
1600 : 8 : ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
1601 : : Anum_pg_propgraph_element_label_oid,
1602 : : ObjectIdGetDatum(peoid),
1603 : : ObjectIdGetDatum(labeloid));
1604 [ - + ]: 8 : if (!ellabeloid)
1605 [ # # ]: 0 : ereport(ERROR,
1606 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1607 : : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1608 : : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
1609 : : parser_errposition(pstate, -1));
1610 : :
1611 : 8 : pgerelid = get_element_relid(peoid);
1612 : :
1613 : 8 : insert_property_records(pgrelid, ellabeloid, pgerelid, stmt->add_properties);
1614 : :
1615 : 8 : CommandCounterIncrement();
1616 : 8 : check_element_properties(peoid);
1617 : 8 : check_element_label_properties(ellabeloid);
1618 : : }
1619 : :
1620 [ + + ]: 104 : if (stmt->drop_properties)
1621 : : {
1622 : : Oid peoid;
1623 : : Oid labeloid;
1624 : 24 : Oid ellabeloid = InvalidOid;
1625 : : ObjectAddress obj;
1626 : :
1627 [ + + ]: 24 : if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
1628 : 16 : peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
1629 : : else
1630 : 8 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1631 : :
1632 : 24 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
1633 : : Anum_pg_propgraph_label_oid,
1634 : : ObjectIdGetDatum(pgrelid),
1635 : : CStringGetDatum(stmt->alter_label));
1636 [ + - ]: 24 : if (labeloid)
1637 : 24 : ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
1638 : : Anum_pg_propgraph_element_label_oid,
1639 : : ObjectIdGetDatum(peoid),
1640 : : ObjectIdGetDatum(labeloid));
1641 : :
1642 [ - + ]: 24 : if (!ellabeloid)
1643 [ # # ]: 0 : ereport(ERROR,
1644 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1645 : : errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
1646 : : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
1647 : : parser_errposition(pstate, -1));
1648 : :
1649 [ + - + + : 44 : foreach(lc, stmt->drop_properties)
+ + ]
1650 : : {
1651 : 24 : char *propname = strVal(lfirst(lc));
1652 : : Oid propoid;
1653 : 24 : Oid plpoid = InvalidOid;
1654 : :
1655 : 24 : propoid = GetSysCacheOid2(PROPGRAPHPROPNAME,
1656 : : Anum_pg_propgraph_property_oid,
1657 : : ObjectIdGetDatum(pgrelid),
1658 : : CStringGetDatum(propname));
1659 [ + - ]: 24 : if (propoid)
1660 : 24 : plpoid = GetSysCacheOid2(PROPGRAPHLABELPROP,
1661 : : Anum_pg_propgraph_label_property_oid,
1662 : : ObjectIdGetDatum(ellabeloid),
1663 : : ObjectIdGetDatum(propoid));
1664 [ + + ]: 24 : if (!plpoid)
1665 [ + - ]: 4 : ereport(ERROR,
1666 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1667 : : errmsg("property graph \"%s\" element \"%s\" label \"%s\" has no property \"%s\"",
1668 : : get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label, propname),
1669 : : parser_errposition(pstate, -1));
1670 : :
1671 : 20 : ObjectAddressSet(obj, PropgraphLabelPropertyRelationId, plpoid);
1672 : 20 : performDeletion(&obj, stmt->drop_behavior, 0);
1673 : : }
1674 : :
1675 : 20 : check_element_label_properties(ellabeloid);
1676 : : }
1677 : :
1678 : : /* Remove any orphaned pg_propgraph_property entries */
1679 [ + + + + : 100 : if (stmt->drop_properties || stmt->drop_vertex_tables || stmt->drop_edge_tables || stmt->drop_label)
+ + + + ]
1680 : : {
1681 [ + - + + : 432 : foreach_oid(propoid, get_graph_property_ids(pgrelid))
+ + ]
1682 : : {
1683 : : Relation rel;
1684 : : SysScanDesc scan;
1685 : : ScanKeyData key[1];
1686 : :
1687 : 352 : rel = table_open(PropgraphLabelPropertyRelationId, RowShareLock);
1688 : 352 : ScanKeyInit(&key[0],
1689 : : Anum_pg_propgraph_label_property_plppropid,
1690 : : BTEqualStrategyNumber, F_OIDEQ,
1691 : : ObjectIdGetDatum(propoid));
1692 : : /* XXX no suitable index */
1693 : 352 : scan = systable_beginscan(rel, InvalidOid, true, NULL, 1, key);
1694 [ + + ]: 352 : if (!systable_getnext(scan))
1695 : : {
1696 : : ObjectAddress obj;
1697 : :
1698 : 32 : ObjectAddressSet(obj, PropgraphPropertyRelationId, propoid);
1699 : 32 : performDeletion(&obj, stmt->drop_behavior, 0);
1700 : : }
1701 : :
1702 : 344 : systable_endscan(scan);
1703 : 344 : table_close(rel, RowShareLock);
1704 : : }
1705 : : }
1706 : :
1707 : : /*
1708 : : * Invalidate relcache entry of the property graph so that the queries in
1709 : : * the cached plans referencing the property graph will be rewritten
1710 : : * considering changes to the property graph.
1711 : : */
1712 : 92 : CacheInvalidateRelcacheByRelid(pgrelid);
1713 : :
1714 : 92 : return pgaddress;
1715 : : }
1716 : :
1717 : : /*
1718 : : * Get OID of vertex from graph OID and element alias. Element must be a
1719 : : * vertex, otherwise error.
1720 : : */
1721 : : static Oid
1722 : 160 : get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
1723 : : {
1724 : : HeapTuple tuple;
1725 : : Oid peoid;
1726 : :
1727 : 160 : tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
1728 [ - + ]: 160 : if (!tuple)
1729 [ # # ]: 0 : ereport(ERROR,
1730 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1731 : : errmsg("property graph \"%s\" has no element with alias \"%s\"",
1732 : : get_rel_name(pgrelid), alias),
1733 : : parser_errposition(pstate, location));
1734 : :
1735 [ - + ]: 160 : if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_VERTEX)
1736 [ # # ]: 0 : ereport(ERROR,
1737 : : errcode(ERRCODE_SYNTAX_ERROR),
1738 : : errmsg("element \"%s\" of property graph \"%s\" is not a vertex",
1739 : : alias, get_rel_name(pgrelid)),
1740 : : parser_errposition(pstate, location));
1741 : :
1742 : 160 : peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
1743 : :
1744 : 160 : ReleaseSysCache(tuple);
1745 : :
1746 : 160 : return peoid;
1747 : : }
1748 : :
1749 : : /*
1750 : : * Get OID of edge from graph OID and element alias. Element must be an edge,
1751 : : * otherwise error.
1752 : : */
1753 : : static Oid
1754 : 24 : get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
1755 : : {
1756 : : HeapTuple tuple;
1757 : : Oid peoid;
1758 : :
1759 : 24 : tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
1760 [ - + ]: 24 : if (!tuple)
1761 [ # # ]: 0 : ereport(ERROR,
1762 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1763 : : errmsg("property graph \"%s\" has no element with alias \"%s\"",
1764 : : get_rel_name(pgrelid), alias),
1765 : : parser_errposition(pstate, location));
1766 : :
1767 [ - + ]: 24 : if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_EDGE)
1768 [ # # ]: 0 : ereport(ERROR,
1769 : : errcode(ERRCODE_SYNTAX_ERROR),
1770 : : errmsg("element \"%s\" of property graph \"%s\" is not an edge",
1771 : : alias, get_rel_name(pgrelid)),
1772 : : parser_errposition(pstate, location));
1773 : :
1774 : 24 : peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
1775 : :
1776 : 24 : ReleaseSysCache(tuple);
1777 : :
1778 : 24 : return peoid;
1779 : : }
1780 : :
1781 : : /*
1782 : : * Get the element table relation OID from the OID of the element.
1783 : : */
1784 : : static Oid
1785 : 112 : get_element_relid(Oid peid)
1786 : : {
1787 : : HeapTuple tuple;
1788 : : Oid pgerelid;
1789 : :
1790 : 112 : tuple = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peid));
1791 [ - + ]: 112 : if (!tuple)
1792 [ # # ]: 0 : elog(ERROR, "cache lookup failed for property graph element %u", peid);
1793 : :
1794 : 112 : pgerelid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgerelid;
1795 : :
1796 : 112 : ReleaseSysCache(tuple);
1797 : :
1798 : 112 : return pgerelid;
1799 : : }
1800 : :
1801 : : /*
1802 : : * Get a list of all label OIDs of a graph.
1803 : : */
1804 : : static List *
1805 : 210 : get_graph_label_ids(Oid graphid)
1806 : : {
1807 : : Relation rel;
1808 : : SysScanDesc scan;
1809 : : ScanKeyData key[1];
1810 : : HeapTuple tuple;
1811 : 210 : List *result = NIL;
1812 : :
1813 : 210 : rel = table_open(PropgraphLabelRelationId, AccessShareLock);
1814 : 210 : ScanKeyInit(&key[0],
1815 : : Anum_pg_propgraph_label_pglpgid,
1816 : : BTEqualStrategyNumber,
1817 : : F_OIDEQ, ObjectIdGetDatum(graphid));
1818 : 210 : scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId, true, NULL, 1, key);
1819 [ + + ]: 896 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1820 : : {
1821 : 686 : result = lappend_oid(result, ((Form_pg_propgraph_label) GETSTRUCT(tuple))->oid);
1822 : : }
1823 : 210 : systable_endscan(scan);
1824 : 210 : table_close(rel, AccessShareLock);
1825 : :
1826 : 210 : return result;
1827 : : }
1828 : :
1829 : : /*
1830 : : * Get a list of all element label OIDs for a label.
1831 : : */
1832 : : static List *
1833 : 706 : get_label_element_label_ids(Oid labelid)
1834 : : {
1835 : : Relation rel;
1836 : : SysScanDesc scan;
1837 : : ScanKeyData key[1];
1838 : : HeapTuple tuple;
1839 : 706 : List *result = NIL;
1840 : :
1841 : 706 : rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1842 : 706 : ScanKeyInit(&key[0],
1843 : : Anum_pg_propgraph_element_label_pgellabelid,
1844 : : BTEqualStrategyNumber,
1845 : : F_OIDEQ, ObjectIdGetDatum(labelid));
1846 : 706 : scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
1847 [ + + ]: 1765 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1848 : : {
1849 : 1059 : result = lappend_oid(result, ((Form_pg_propgraph_element_label) GETSTRUCT(tuple))->oid);
1850 : : }
1851 : 706 : systable_endscan(scan);
1852 : 706 : table_close(rel, AccessShareLock);
1853 : :
1854 : 706 : return result;
1855 : : }
1856 : :
1857 : : /*
1858 : : * Get the names of properties associated with the given element label OID.
1859 : : *
1860 : : * The result is a list of String nodes (so we can use list functions to
1861 : : * detect differences).
1862 : : */
1863 : : static List *
1864 : 908 : get_element_label_property_names(Oid ellabeloid)
1865 : : {
1866 : : Relation rel;
1867 : : SysScanDesc scan;
1868 : : ScanKeyData key[1];
1869 : : HeapTuple tuple;
1870 : 908 : List *result = NIL;
1871 : :
1872 : 908 : rel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
1873 : :
1874 : 908 : ScanKeyInit(&key[0],
1875 : : Anum_pg_propgraph_label_property_plpellabelid,
1876 : : BTEqualStrategyNumber, F_OIDEQ,
1877 : : ObjectIdGetDatum(ellabeloid));
1878 : :
1879 : 908 : scan = systable_beginscan(rel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key);
1880 : :
1881 [ + + ]: 2350 : while ((tuple = systable_getnext(scan)))
1882 : : {
1883 : 1442 : Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tuple);
1884 : :
1885 : 1442 : result = lappend(result, makeString(get_propgraph_property_name(plpform->plppropid)));
1886 : : }
1887 : :
1888 : 908 : systable_endscan(scan);
1889 : 908 : table_close(rel, AccessShareLock);
1890 : :
1891 : 908 : return result;
1892 : : }
1893 : :
1894 : : /*
1895 : : * Get a list of all property OIDs of a graph.
1896 : : */
1897 : : static List *
1898 : 48 : get_graph_property_ids(Oid graphid)
1899 : : {
1900 : : Relation rel;
1901 : : SysScanDesc scan;
1902 : : ScanKeyData key[1];
1903 : : HeapTuple tuple;
1904 : 48 : List *result = NIL;
1905 : :
1906 : 48 : rel = table_open(PropgraphPropertyRelationId, AccessShareLock);
1907 : 48 : ScanKeyInit(&key[0],
1908 : : Anum_pg_propgraph_property_pgppgid,
1909 : : BTEqualStrategyNumber,
1910 : : F_OIDEQ, ObjectIdGetDatum(graphid));
1911 : 48 : scan = systable_beginscan(rel, PropgraphPropertyNameIndexId, true, NULL, 1, key);
1912 [ + + ]: 484 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1913 : : {
1914 : 436 : result = lappend_oid(result, ((Form_pg_propgraph_property) GETSTRUCT(tuple))->oid);
1915 : : }
1916 : 48 : systable_endscan(scan);
1917 : 48 : table_close(rel, AccessShareLock);
1918 : :
1919 : 48 : return result;
1920 : : }
|