Age Owner Branch data TLA 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
50 peter@eisentraut.org 104 :GNC 200 : CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt)
105 : : {
106 : 200 : CreateStmt *cstmt = makeNode(CreateStmt);
107 : : char components_persistence;
108 : : ListCell *lc;
109 : : ObjectAddress pgaddress;
110 : 200 : List *vertex_infos = NIL;
111 : 200 : List *edge_infos = NIL;
112 : 200 : List *element_aliases = NIL;
113 : 200 : List *element_oids = NIL;
114 : :
115 [ + + ]: 200 : 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 : 196 : components_persistence = RELPERSISTENCE_PERMANENT;
121 : :
122 [ + + + + : 536 : foreach(lc, stmt->vertex_tables)
+ + ]
123 : : {
124 : 348 : PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
125 : : struct element_info *vinfo;
126 : : Relation rel;
127 : :
128 : 348 : vinfo = palloc0_object(struct element_info);
129 : 348 : vinfo->kind = PGEKIND_VERTEX;
130 : :
131 : 348 : vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
132 : :
133 : 344 : rel = table_open(vinfo->relid, NoLock);
134 : :
135 [ + + ]: 344 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
136 : 8 : components_persistence = RELPERSISTENCE_TEMP;
137 : :
138 [ + + ]: 344 : if (vertex->vtable->alias)
139 : 8 : vinfo->aliasname = vertex->vtable->alias->aliasname;
140 : : else
141 : 336 : vinfo->aliasname = vertex->vtable->relname;
142 : :
143 [ + + ]: 344 : 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 : 340 : vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location);
150 : :
151 : 340 : vinfo->labels = vertex->labels;
152 : :
153 : 340 : table_close(rel, NoLock);
154 : :
155 : 340 : vertex_infos = lappend(vertex_infos, vinfo);
156 : :
157 : 340 : element_aliases = lappend(element_aliases, makeString(vinfo->aliasname));
158 : : }
159 : :
160 [ + + + + : 344 : foreach(lc, stmt->edge_tables)
+ + ]
161 : : {
162 : 176 : 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 : 176 : einfo = palloc0_object(struct element_info);
172 : 176 : einfo->kind = PGEKIND_EDGE;
173 : :
174 : 176 : einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
175 : :
176 : 176 : rel = table_open(einfo->relid, NoLock);
177 : :
178 [ - + ]: 176 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
50 peter@eisentraut.org 179 :UNC 0 : components_persistence = RELPERSISTENCE_TEMP;
180 : :
50 peter@eisentraut.org 181 [ - + ]:GNC 176 : if (edge->etable->alias)
50 peter@eisentraut.org 182 :UNC 0 : einfo->aliasname = edge->etable->alias->aliasname;
183 : : else
50 peter@eisentraut.org 184 :GNC 176 : einfo->aliasname = edge->etable->relname;
185 : :
186 [ - + ]: 176 : if (list_member(element_aliases, makeString(einfo->aliasname)))
50 peter@eisentraut.org 187 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 192 :GNC 176 : einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location);
193 : :
194 : 176 : einfo->srcvertex = edge->esrcvertex;
195 : 176 : einfo->destvertex = edge->edestvertex;
196 : :
197 : 176 : srcrelid = 0;
198 : 176 : destrelid = 0;
199 [ + - + + : 421 : foreach(lc2, vertex_infos)
+ + ]
200 : : {
201 : 413 : struct element_info *vinfo = lfirst(lc2);
202 : :
203 [ + + ]: 413 : if (strcmp(vinfo->aliasname, edge->esrcvertex) == 0)
204 : 172 : srcrelid = vinfo->relid;
205 : :
206 [ + + ]: 413 : if (strcmp(vinfo->aliasname, edge->edestvertex) == 0)
207 : 172 : destrelid = vinfo->relid;
208 : :
209 [ + + + + ]: 413 : if (srcrelid && destrelid)
210 : 168 : break;
211 : : }
212 [ + + ]: 176 : 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 [ + + ]: 172 : 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 : 168 : srcrel = table_open(srcrelid, NoLock);
226 : 168 : destrel = table_open(destrelid, NoLock);
227 : :
228 : 168 : propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel,
229 : 168 : einfo->aliasname, edge->location, "SOURCE",
230 : : &einfo->srckey, &einfo->srcref, &einfo->srceqop);
231 : 160 : propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel,
232 : 160 : einfo->aliasname, edge->location, "DESTINATION",
233 : : &einfo->destkey, &einfo->destref, &einfo->desteqop);
234 : :
235 : 156 : einfo->labels = edge->labels;
236 : :
237 : 156 : table_close(destrel, NoLock);
238 : 156 : table_close(srcrel, NoLock);
239 : :
240 : 156 : table_close(rel, NoLock);
241 : :
242 : 156 : edge_infos = lappend(edge_infos, einfo);
243 : :
244 : 156 : element_aliases = lappend(element_aliases, makeString(einfo->aliasname));
245 : : }
246 : :
247 : 168 : cstmt->relation = stmt->pgname;
248 : 168 : cstmt->oncommit = ONCOMMIT_NOOP;
249 : :
250 : : /*
251 : : * Automatically make it temporary if any component tables are temporary
252 : : * (see also DefineView()).
253 : : */
254 [ + + ]: 168 : if (stmt->pgname->relpersistence == RELPERSISTENCE_PERMANENT
255 [ + + ]: 156 : && 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 : 168 : pgaddress = DefineRelation(cstmt, RELKIND_PROPGRAPH, InvalidOid, NULL, NULL);
265 : :
266 [ + + + + : 440 : foreach(lc, vertex_infos)
+ + ]
267 : : {
268 : 292 : struct element_info *vinfo = lfirst(lc);
269 : : Oid peoid;
270 : :
271 : 292 : peoid = insert_element_record(pgaddress, vinfo);
272 : 276 : element_oids = lappend_oid(element_oids, peoid);
273 : : }
274 : :
275 [ + + + + : 300 : foreach(lc, edge_infos)
+ + ]
276 : : {
277 : 156 : 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 [ + - + - : 373 : foreach(lc2, vertex_infos)
+ - ]
286 : : {
287 : 373 : struct element_info *vinfo = lfirst(lc2);
288 : :
289 [ + + ]: 373 : if (strcmp(vinfo->aliasname, einfo->srcvertex) == 0)
290 : : {
291 : 156 : einfo->srcvertexid = vinfo->elementid;
292 : 156 : einfo->srcrelid = vinfo->relid;
293 : : }
294 [ + + ]: 373 : if (strcmp(vinfo->aliasname, einfo->destvertex) == 0)
295 : : {
296 : 156 : einfo->destvertexid = vinfo->elementid;
297 : 156 : einfo->destrelid = vinfo->relid;
298 : : }
299 [ + + + + ]: 373 : if (einfo->srcvertexid && einfo->destvertexid)
300 : 156 : break;
301 : : }
302 [ - + ]: 156 : Assert(einfo->srcvertexid);
303 [ - + ]: 156 : Assert(einfo->destvertexid);
304 [ - + ]: 156 : Assert(einfo->srcrelid);
305 [ - + ]: 156 : Assert(einfo->destrelid);
306 : 156 : peoid = insert_element_record(pgaddress, einfo);
307 : 152 : element_oids = lappend_oid(element_oids, peoid);
308 : : }
309 : :
310 : 144 : CommandCounterIncrement();
311 : :
312 [ + + + + : 676 : foreach_oid(peoid, element_oids)
+ + ]
313 : 396 : check_element_properties(peoid);
314 : 140 : check_all_labels_properties(pgaddress.objectId);
315 : :
316 : 128 : 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 : 580 : propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, const char *aliasname, int location)
326 : : {
327 : : ArrayType *a;
328 : :
329 [ + + ]: 580 : if (key_clause == NIL)
330 : : {
331 : 114 : Oid pkidx = RelationGetPrimaryKeyIndex(element_rel, false);
332 : :
333 [ - + ]: 114 : if (!pkidx)
50 peter@eisentraut.org 334 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 342 :GNC 114 : indexDesc = index_open(pkidx, AccessShareLock);
343 : 114 : a = array_from_attnums(indexDesc->rd_index->indkey.dim1, indexDesc->rd_index->indkey.values);
344 : 114 : index_close(indexDesc, NoLock);
345 : : }
346 : : }
347 : : else
348 : : {
349 : 466 : a = array_from_attnums(list_length(key_clause),
350 : 466 : array_from_column_list(pstate, key_clause, location, element_rel));
351 : : }
352 : :
353 : 580 : 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 : 400 : 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 [ + + - + : 400 : Assert((keycols && refcols) || (!keycols && !refcols));
+ - - + ]
383 : :
384 [ + + ]: 400 : if (keycols)
385 : : {
386 [ - + ]: 388 : if (list_length(keycols) != list_length(refcols))
50 peter@eisentraut.org 387 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 392 :GNC 388 : nkeys = list_length(keycols);
393 : 388 : keyattnums = array_from_column_list(pstate, keycols, location, edge_rel);
394 : 388 : refattnums = array_from_column_list(pstate, refcols, location, ref_rel);
395 : 388 : keyeqops = palloc_array(Oid, nkeys);
396 : :
397 [ + + ]: 816 : 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 vertexes.
423 : : */
424 : 436 : get_atttypetypmodcoll(RelationGetRelid(edge_rel), keyattnums[i], &keytype, &keytypmod, &keycoll);
425 : 436 : get_atttypetypmodcoll(RelationGetRelid(ref_rel), refattnums[i], &reftype, &reftypmod, &refcoll);
426 : 436 : keyeqops[i] = InvalidOid;
427 : 436 : strategy = BTEqualStrategyNumber;
428 : 436 : opc = GetDefaultOpClass(reftype, BTREE_AM_OID);
429 [ - + ]: 436 : if (!OidIsValid(opc))
430 : : {
50 peter@eisentraut.org 431 :UNC 0 : opc = GetDefaultOpClass(reftype, HASH_AM_OID);
432 : 0 : strategy = HTEqualStrategyNumber;
433 : : }
50 peter@eisentraut.org 434 [ + - ]:GNC 436 : if (OidIsValid(opc))
435 : : {
436 : 436 : opf = get_opclass_family(opc);
437 [ + - ]: 436 : if (OidIsValid(opf))
438 : : {
439 : 436 : keyeqops[i] = get_opfamily_member(opf, reftype, keytype, strategy);
440 [ + + ]: 436 : if (!OidIsValid(keyeqops[i]))
441 : : {
442 : : /* Last resort, implicit cast. */
443 [ - + ]: 4 : if (can_coerce_type(1, &keytype, &reftype, COERCION_IMPLICIT))
50 peter@eisentraut.org 444 :UNC 0 : keyeqops[i] = get_opfamily_member(opf, reftype, reftype, strategy);
445 : : }
446 : : }
447 : : }
448 : :
50 peter@eisentraut.org 449 [ + + ]:GNC 436 : 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 [ + + ]: 432 : 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)
50 peter@eisentraut.org 485 [ # # ]:UNC 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));
50 peter@eisentraut.org 489 :GNC 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 : 388 : *outkey = array_from_attnums(nkeys, keyattnums);
506 : 388 : *outref = array_from_attnums(nkeys, refattnums);
507 : 388 : datums = palloc_array(Datum, nkeys);
508 [ + + ]: 824 : for (int i = 0; i < nkeys; i++)
509 : 436 : datums[i] = ObjectIdGetDatum(keyeqops[i]);
510 : 388 : *outeqop = construct_array_builtin(datums, nkeys, OIDOID);
511 : 388 : }
512 : :
513 : : /*
514 : : * Convert list of column names in the specified relation into an array of
515 : : * column numbers.
516 : : */
517 : : static AttrNumber *
518 : 1242 : 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 : 1242 : numattrs = list_length(colnames);
526 : 1242 : attnums = palloc_array(AttrNumber, numattrs);
527 : :
528 : 1242 : i = 0;
529 [ + - + + : 2741 : foreach(lc, colnames)
+ + ]
530 : : {
531 : 1499 : char *colname = strVal(lfirst(lc));
532 : 1499 : Oid relid = RelationGetRelid(element_rel);
533 : : AttrNumber attnum;
534 : :
535 : 1499 : attnum = get_attnum(relid, colname);
536 [ - + ]: 1499 : if (!attnum)
50 peter@eisentraut.org 537 [ # # ]:UNC 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)));
50 peter@eisentraut.org 542 :GNC 1499 : attnums[i++] = attnum;
543 : : }
544 : :
545 [ + + ]: 2741 : for (int j = 0; j < numattrs; j++)
546 : : {
547 [ + + ]: 1790 : for (int k = j + 1; k < numattrs; k++)
548 : : {
549 [ - + ]: 291 : if (attnums[j] == attnums[k])
50 peter@eisentraut.org 550 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 557 :GNC 1242 : return attnums;
558 : : }
559 : :
560 : : static ArrayType *
561 : 1356 : array_from_attnums(int numattrs, const AttrNumber *attnums)
562 : : {
563 : : Datum *attnumsd;
564 : :
565 : 1356 : attnumsd = palloc_array(Datum, numattrs);
566 : :
567 [ + + ]: 3001 : for (int i = 0; i < numattrs; i++)
568 : 1645 : attnumsd[i] = Int16GetDatum(attnums[i]);
569 : :
570 : 1356 : return construct_array_builtin(attnumsd, numattrs, INT2OID);
571 : : }
572 : :
573 : : static void
574 : 1276 : array_of_attnums_to_objectaddrs(Oid relid, ArrayType *arr, ObjectAddresses *addrs)
575 : : {
576 : : Datum *attnumsd;
577 : : int numattrs;
578 : :
579 : 1276 : deconstruct_array_builtin(arr, INT2OID, &attnumsd, NULL, &numattrs);
580 : :
581 [ + + ]: 2821 : for (int i = 0; i < numattrs; i++)
582 : : {
583 : : ObjectAddress referenced;
584 : :
585 : 1545 : ObjectAddressSubSet(referenced, RelationRelationId, relid, DatumGetInt16(attnumsd[i]));
586 : 1545 : add_exact_object_address(&referenced, addrs);
587 : : }
588 : 1276 : }
589 : :
590 : : static void
591 : 384 : array_of_opers_to_objectaddrs(ArrayType *arr, ObjectAddresses *addrs)
592 : : {
593 : : Datum *opersd;
594 : : int numopers;
595 : :
596 : 384 : deconstruct_array_builtin(arr, OIDOID, &opersd, NULL, &numopers);
597 : :
598 [ + + ]: 816 : for (int i = 0; i < numopers; i++)
599 : : {
600 : : ObjectAddress referenced;
601 : :
602 : 432 : ObjectAddressSet(referenced, OperatorRelationId, DatumGetObjectId(opersd[i]));
603 : 432 : add_exact_object_address(&referenced, addrs);
604 : : }
605 : 384 : }
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 : 508 : insert_element_record(ObjectAddress pgaddress, struct element_info *einfo)
613 : : {
614 : 508 : Oid graphid = pgaddress.objectId;
615 : : Relation rel;
616 : : NameData aliasname;
617 : : Oid peoid;
618 : 508 : Datum values[Natts_pg_propgraph_element] = {0};
619 : 508 : bool nulls[Natts_pg_propgraph_element] = {0};
620 : : HeapTuple tup;
621 : : ObjectAddress myself;
622 : : ObjectAddress referenced;
623 : : ObjectAddresses *addrs;
624 : :
625 : 508 : rel = table_open(PropgraphElementRelationId, RowExclusiveLock);
626 : :
627 : 508 : peoid = GetNewOidWithIndex(rel, PropgraphElementObjectIndexId, Anum_pg_propgraph_element_oid);
628 : 508 : einfo->elementid = peoid;
629 : 508 : values[Anum_pg_propgraph_element_oid - 1] = ObjectIdGetDatum(peoid);
630 : 508 : values[Anum_pg_propgraph_element_pgepgid - 1] = ObjectIdGetDatum(graphid);
631 : 508 : values[Anum_pg_propgraph_element_pgerelid - 1] = ObjectIdGetDatum(einfo->relid);
632 : 508 : namestrcpy(&aliasname, einfo->aliasname);
633 : 508 : values[Anum_pg_propgraph_element_pgealias - 1] = NameGetDatum(&aliasname);
634 : 508 : values[Anum_pg_propgraph_element_pgekind - 1] = CharGetDatum(einfo->kind);
635 : 508 : values[Anum_pg_propgraph_element_pgesrcvertexid - 1] = ObjectIdGetDatum(einfo->srcvertexid);
636 : 508 : values[Anum_pg_propgraph_element_pgedestvertexid - 1] = ObjectIdGetDatum(einfo->destvertexid);
637 : 508 : values[Anum_pg_propgraph_element_pgekey - 1] = PointerGetDatum(einfo->key);
638 : :
639 [ + + ]: 508 : if (einfo->srckey)
640 : 192 : values[Anum_pg_propgraph_element_pgesrckey - 1] = PointerGetDatum(einfo->srckey);
641 : : else
642 : 316 : nulls[Anum_pg_propgraph_element_pgesrckey - 1] = true;
643 [ + + ]: 508 : if (einfo->srcref)
644 : 192 : values[Anum_pg_propgraph_element_pgesrcref - 1] = PointerGetDatum(einfo->srcref);
645 : : else
646 : 316 : nulls[Anum_pg_propgraph_element_pgesrcref - 1] = true;
647 [ + + ]: 508 : if (einfo->srceqop)
648 : 192 : values[Anum_pg_propgraph_element_pgesrceqop - 1] = PointerGetDatum(einfo->srceqop);
649 : : else
650 : 316 : nulls[Anum_pg_propgraph_element_pgesrceqop - 1] = true;
651 [ + + ]: 508 : if (einfo->destkey)
652 : 192 : values[Anum_pg_propgraph_element_pgedestkey - 1] = PointerGetDatum(einfo->destkey);
653 : : else
654 : 316 : nulls[Anum_pg_propgraph_element_pgedestkey - 1] = true;
655 [ + + ]: 508 : if (einfo->destref)
656 : 192 : values[Anum_pg_propgraph_element_pgedestref - 1] = PointerGetDatum(einfo->destref);
657 : : else
658 : 316 : nulls[Anum_pg_propgraph_element_pgedestref - 1] = true;
659 [ + + ]: 508 : if (einfo->desteqop)
660 : 192 : values[Anum_pg_propgraph_element_pgedesteqop - 1] = PointerGetDatum(einfo->desteqop);
661 : : else
662 : 316 : nulls[Anum_pg_propgraph_element_pgedesteqop - 1] = true;
663 : :
664 : 508 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
665 : 508 : CatalogTupleInsert(rel, tup);
666 : 508 : heap_freetuple(tup);
667 : :
668 : 508 : ObjectAddressSet(myself, PropgraphElementRelationId, peoid);
669 : :
670 : : /* Add dependency on the property graph */
671 : 508 : recordDependencyOn(&myself, &pgaddress, DEPENDENCY_AUTO);
672 : :
673 : 508 : addrs = new_object_addresses();
674 : :
675 : : /* Add dependency on the relation */
676 : 508 : ObjectAddressSet(referenced, RelationRelationId, einfo->relid);
677 : 508 : add_exact_object_address(&referenced, addrs);
678 : 508 : 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 [ + + ]: 508 : if (einfo->srcvertexid)
685 : : {
686 : 192 : ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->srcvertexid);
687 : 192 : add_exact_object_address(&referenced, addrs);
688 : 192 : array_of_attnums_to_objectaddrs(einfo->relid, einfo->srckey, addrs);
689 : 192 : array_of_attnums_to_objectaddrs(einfo->srcrelid, einfo->srcref, addrs);
690 : 192 : array_of_opers_to_objectaddrs(einfo->srceqop, addrs);
691 : : }
692 [ + + ]: 508 : if (einfo->destvertexid)
693 : : {
694 : 192 : ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->destvertexid);
695 : 192 : add_exact_object_address(&referenced, addrs);
696 : 192 : array_of_attnums_to_objectaddrs(einfo->relid, einfo->destkey, addrs);
697 : 192 : array_of_attnums_to_objectaddrs(einfo->destrelid, einfo->destref, addrs);
698 : 192 : array_of_opers_to_objectaddrs(einfo->desteqop, addrs);
699 : : }
700 : :
701 : 508 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
702 : :
703 : 508 : table_close(rel, NoLock);
704 : :
705 [ + - ]: 508 : if (einfo->labels)
706 : : {
707 : : ListCell *lc;
708 : :
709 [ + - + + : 1104 : foreach(lc, einfo->labels)
+ + ]
710 : : {
711 : 624 : PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
712 : : Oid ellabeloid;
713 : :
714 [ + + ]: 624 : if (lp->label)
715 : 256 : ellabeloid = insert_label_record(graphid, peoid, lp->label);
716 : : else
717 : 368 : ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname);
718 : 624 : insert_property_records(graphid, ellabeloid, einfo->relid, lp->properties);
719 : :
720 : 596 : CommandCounterIncrement();
721 : : }
722 : : }
723 : : else
724 : : {
725 : : Oid ellabeloid;
50 peter@eisentraut.org 726 :UNC 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 : :
50 peter@eisentraut.org 735 :GNC 480 : 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 : 652 : 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 : 652 : labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(graphid), CStringGetDatum(label));
754 [ + + ]: 652 : if (!labeloid)
755 : : {
756 : : Relation rel;
757 : 503 : Datum values[Natts_pg_propgraph_label] = {0};
758 : 503 : bool nulls[Natts_pg_propgraph_label] = {0};
759 : : NameData labelname;
760 : : HeapTuple tup;
761 : : ObjectAddress myself;
762 : : ObjectAddress referenced;
763 : :
764 : 503 : rel = table_open(PropgraphLabelRelationId, RowExclusiveLock);
765 : :
766 : 503 : labeloid = GetNewOidWithIndex(rel, PropgraphLabelObjectIndexId, Anum_pg_propgraph_label_oid);
767 : 503 : values[Anum_pg_propgraph_label_oid - 1] = ObjectIdGetDatum(labeloid);
768 : 503 : values[Anum_pg_propgraph_label_pglpgid - 1] = ObjectIdGetDatum(graphid);
769 : 503 : namestrcpy(&labelname, label);
770 : 503 : values[Anum_pg_propgraph_label_pgllabel - 1] = NameGetDatum(&labelname);
771 : :
772 : 503 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
773 : 503 : CatalogTupleInsert(rel, tup);
774 : 503 : heap_freetuple(tup);
775 : :
776 : 503 : ObjectAddressSet(myself, PropgraphLabelRelationId, labeloid);
777 : :
778 : 503 : ObjectAddressSet(referenced, RelationRelationId, graphid);
779 : 503 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
780 : :
781 : 503 : table_close(rel, NoLock);
782 : : }
783 : :
784 : : /*
785 : : * Insert into pg_propgraph_element_label
786 : : */
787 : : {
788 : : Relation rel;
789 : 652 : Datum values[Natts_pg_propgraph_element_label] = {0};
790 : 652 : bool nulls[Natts_pg_propgraph_element_label] = {0};
791 : : HeapTuple tup;
792 : : ObjectAddress myself;
793 : : ObjectAddress referenced;
794 : :
795 : 652 : rel = table_open(PropgraphElementLabelRelationId, RowExclusiveLock);
796 : :
797 : 652 : ellabeloid = GetNewOidWithIndex(rel, PropgraphElementLabelObjectIndexId, Anum_pg_propgraph_element_label_oid);
798 : 652 : values[Anum_pg_propgraph_element_label_oid - 1] = ObjectIdGetDatum(ellabeloid);
799 : 652 : values[Anum_pg_propgraph_element_label_pgellabelid - 1] = ObjectIdGetDatum(labeloid);
800 : 652 : values[Anum_pg_propgraph_element_label_pgelelid - 1] = ObjectIdGetDatum(peoid);
801 : :
802 : 652 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
803 : 652 : CatalogTupleInsert(rel, tup);
804 : 652 : heap_freetuple(tup);
805 : :
806 : 652 : ObjectAddressSet(myself, PropgraphElementLabelRelationId, ellabeloid);
807 : :
808 : 652 : ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid);
809 : 652 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
810 : 652 : ObjectAddressSet(referenced, PropgraphElementRelationId, peoid);
811 : 652 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
812 : :
813 : 652 : table_close(rel, NoLock);
814 : : }
815 : :
816 : 652 : return ellabeloid;
817 : : }
818 : :
819 : : /*
820 : : * Insert records for properties into the pg_propgraph_property catalog.
821 : : */
822 : : static void
823 : 660 : insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties)
824 : : {
825 : 660 : List *proplist = NIL;
826 : : ParseState *pstate;
827 : : ParseNamespaceItem *nsitem;
828 : : List *tp;
829 : : Relation rel;
830 : : ListCell *lc;
831 : :
832 [ + + ]: 660 : if (properties->all)
833 : : {
834 : : Relation attRelation;
835 : : SysScanDesc scan;
836 : : ScanKeyData key[1];
837 : : HeapTuple attributeTuple;
838 : :
839 : 283 : attRelation = table_open(AttributeRelationId, RowShareLock);
840 : 283 : ScanKeyInit(&key[0],
841 : : Anum_pg_attribute_attrelid,
842 : : BTEqualStrategyNumber, F_OIDEQ,
843 : : ObjectIdGetDatum(pgerelid));
844 : 283 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
845 : : true, NULL, 1, key);
846 [ + + ]: 2729 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
847 : : {
848 : 2446 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
849 : : ColumnRef *cr;
850 : : ResTarget *rt;
851 : :
852 [ + + + + ]: 2446 : if (att->attnum <= 0 || att->attisdropped)
853 : 1686 : continue;
854 : :
855 : 760 : cr = makeNode(ColumnRef);
856 : 760 : rt = makeNode(ResTarget);
857 : :
858 : 760 : cr->fields = list_make1(makeString(pstrdup(NameStr(att->attname))));
859 : 760 : cr->location = -1;
860 : :
861 : 760 : rt->name = pstrdup(NameStr(att->attname));
862 : 760 : rt->val = (Node *) cr;
863 : 760 : rt->location = -1;
864 : :
865 : 760 : proplist = lappend(proplist, rt);
866 : : }
867 : 283 : systable_endscan(scan);
868 : 283 : 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))
50 peter@eisentraut.org 879 [ # # ]:UNC 0 : ereport(ERROR,
880 : : errcode(ERRCODE_SYNTAX_ERROR),
881 : : errmsg("property name required"),
882 : : parser_errposition(NULL, rt->location));
883 : : }
884 : : }
885 : :
50 peter@eisentraut.org 886 :GNC 660 : rel = table_open(pgerelid, AccessShareLock);
887 : :
888 : 660 : pstate = make_parsestate(NULL);
889 : 660 : nsitem = addRangeTableEntryForRelation(pstate,
890 : : rel,
891 : : AccessShareLock,
892 : : NULL,
893 : : false,
894 : : true);
895 : 660 : addNSItemToQuery(pstate, nsitem, true, true, true);
896 : :
897 : 660 : table_close(rel, NoLock);
898 : :
899 : 660 : tp = transformTargetList(pstate, proplist, EXPR_KIND_PROPGRAPH_PROPERTY);
900 : 660 : assign_expr_collations(pstate, (Node *) tp);
901 : :
902 [ + + + + : 2022 : foreach(lc, tp)
+ + ]
903 : : {
904 : 1394 : TargetEntry *te = lfirst_node(TargetEntry, lc);
905 : :
906 : 1394 : insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr);
907 : : }
908 : 628 : }
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 : 1394 : insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr)
916 : : {
917 : : Oid propoid;
918 : 1394 : Oid exprtypid = exprType((const Node *) expr);
919 : 1394 : int32 exprtypmod = exprTypmod((const Node *) expr);
920 : 1394 : Oid exprcollation = exprCollation((const Node *) expr);
921 : :
922 : : /*
923 : : * Insert into pg_propgraph_property if not already existing.
924 : : */
925 : 1394 : propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, Anum_pg_propgraph_property_oid, ObjectIdGetDatum(graphid), CStringGetDatum(propname));
926 [ + + ]: 1394 : if (!OidIsValid(propoid))
927 : : {
928 : : Relation rel;
929 : : NameData propnamedata;
930 : 753 : Datum values[Natts_pg_propgraph_property] = {0};
931 : 753 : bool nulls[Natts_pg_propgraph_property] = {0};
932 : : HeapTuple tup;
933 : : ObjectAddress myself;
934 : : ObjectAddress referenced;
935 : :
936 : 753 : rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock);
937 : :
938 : 753 : propoid = GetNewOidWithIndex(rel, PropgraphPropertyObjectIndexId, Anum_pg_propgraph_property_oid);
939 : 753 : values[Anum_pg_propgraph_property_oid - 1] = ObjectIdGetDatum(propoid);
940 : 753 : values[Anum_pg_propgraph_property_pgppgid - 1] = ObjectIdGetDatum(graphid);
941 : 753 : namestrcpy(&propnamedata, propname);
942 : 753 : values[Anum_pg_propgraph_property_pgpname - 1] = NameGetDatum(&propnamedata);
943 : 753 : values[Anum_pg_propgraph_property_pgptypid - 1] = ObjectIdGetDatum(exprtypid);
944 : 753 : values[Anum_pg_propgraph_property_pgptypmod - 1] = Int32GetDatum(exprtypmod);
945 : 753 : values[Anum_pg_propgraph_property_pgpcollation - 1] = ObjectIdGetDatum(exprcollation);
946 : :
947 : 753 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
948 : 753 : CatalogTupleInsert(rel, tup);
949 : 753 : heap_freetuple(tup);
950 : :
951 : 753 : ObjectAddressSet(myself, PropgraphPropertyRelationId, propoid);
952 : :
953 : 753 : ObjectAddressSet(referenced, RelationRelationId, graphid);
954 : 753 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
955 : 753 : ObjectAddressSet(referenced, TypeRelationId, exprtypid);
956 : 753 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
957 [ + + + + ]: 753 : if (OidIsValid(exprcollation) && exprcollation != DEFAULT_COLLATION_OID)
958 : : {
959 : 23 : ObjectAddressSet(referenced, CollationRelationId, exprcollation);
960 : 23 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
961 : : }
962 : :
963 : 753 : table_close(rel, NoLock);
964 : : }
965 : : else
966 : : {
967 : 641 : HeapTuple pgptup = SearchSysCache1(PROPGRAPHPROPOID, ObjectIdGetDatum(propoid));
968 : 641 : Form_pg_propgraph_property pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup);
969 : 641 : Oid proptypid = pgpform->pgptypid;
970 : 641 : int32 proptypmod = pgpform->pgptypmod;
971 : 641 : Oid propcollation = pgpform->pgpcollation;
972 : :
973 : 641 : 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 [ + + + + ]: 641 : 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 [ + + ]: 625 : 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 : 1362 : Datum values[Natts_pg_propgraph_label_property] = {0};
1007 : 1362 : bool nulls[Natts_pg_propgraph_label_property] = {0};
1008 : : Oid plpoid;
1009 : : HeapTuple tup;
1010 : : ObjectAddress myself;
1011 : : ObjectAddress referenced;
1012 : :
1013 : 1362 : rel = table_open(PropgraphLabelPropertyRelationId, RowExclusiveLock);
1014 : :
1015 : 1362 : plpoid = GetNewOidWithIndex(rel, PropgraphLabelPropertyObjectIndexId, Anum_pg_propgraph_label_property_oid);
1016 : 1362 : values[Anum_pg_propgraph_label_property_oid - 1] = ObjectIdGetDatum(plpoid);
1017 : 1362 : values[Anum_pg_propgraph_label_property_plppropid - 1] = ObjectIdGetDatum(propoid);
1018 : 1362 : values[Anum_pg_propgraph_label_property_plpellabelid - 1] = ObjectIdGetDatum(ellabeloid);
1019 : 1362 : values[Anum_pg_propgraph_label_property_plpexpr - 1] = CStringGetTextDatum(nodeToString(expr));
1020 : :
1021 : 1362 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
1022 : 1362 : CatalogTupleInsert(rel, tup);
1023 : 1362 : heap_freetuple(tup);
1024 : :
1025 : 1362 : ObjectAddressSet(myself, PropgraphLabelPropertyRelationId, plpoid);
1026 : :
1027 : 1362 : ObjectAddressSet(referenced, PropgraphPropertyRelationId, propoid);
1028 : 1362 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1029 : :
1030 : 1362 : ObjectAddressSet(referenced, PropgraphElementLabelRelationId, ellabeloid);
1031 : 1362 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1032 : :
1033 : 1362 : recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false);
1034 : :
1035 : 1362 : table_close(rel, NoLock);
1036 : : }
1037 : 1362 : }
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 : : * GRAPH checks all elements it has created.
1049 : : */
1050 : : static void
1051 : 480 : check_element_properties(Oid peoid)
1052 : : {
1053 : : Relation rel1;
1054 : : ScanKeyData key1[1];
1055 : : SysScanDesc scan1;
1056 : : HeapTuple tuple1;
1057 : 480 : List *propoids = NIL;
1058 : 480 : List *propexprs = NIL;
1059 : :
1060 : 480 : rel1 = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1061 : 480 : ScanKeyInit(&key1[0],
1062 : : Anum_pg_propgraph_element_label_pgelelid,
1063 : : BTEqualStrategyNumber, F_OIDEQ,
1064 : : ObjectIdGetDatum(peoid));
1065 : :
1066 : 480 : scan1 = systable_beginscan(rel1, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, key1);
1067 [ + + ]: 1096 : while (HeapTupleIsValid(tuple1 = systable_getnext(scan1)))
1068 : : {
1069 : 624 : 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 : 624 : rel2 = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
1076 : 624 : ScanKeyInit(&key2[0],
1077 : : Anum_pg_propgraph_label_property_plpellabelid,
1078 : : BTEqualStrategyNumber, F_OIDEQ,
1079 : : ObjectIdGetDatum(ellabel->oid));
1080 : :
1081 : 624 : scan2 = systable_beginscan(rel2, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key2);
1082 [ + + ]: 1954 : while (HeapTupleIsValid(tuple2 = systable_getnext(scan2)))
1083 : : {
1084 : 1338 : 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 : 1338 : propoid = lprop->plppropid;
1094 : 1338 : datum = heap_getattr(tuple2, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(rel2), &isnull);
1095 [ - + ]: 1338 : Assert(!isnull);
1096 : 1338 : propexpr = TextDatumGetCString(datum);
1097 : :
1098 : 1338 : found = false;
1099 [ + + + + : 2628 : forboth(lc1, propoids, lc2, propexprs)
+ + + + +
+ + - +
+ ]
1100 : : {
1101 [ + + ]: 1393 : 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)
50 peter@eisentraut.org 1121 [ # # ]:UNC 0 : elog(ERROR, "cache lookup failed for property graph element %u", peoid);
50 peter@eisentraut.org 1122 :GNC 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 : :
50 peter@eisentraut.org 1136 :UNC 0 : tmp = dpa;
1137 : 0 : dpa = dpb;
1138 : 0 : dpb = tmp;
1139 : : }
1140 : :
50 peter@eisentraut.org 1141 [ + - ]:GNC 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 [ + + ]: 1330 : if (!found)
1155 : : {
1156 : 1235 : propoids = lappend_oid(propoids, propoid);
1157 : 1235 : propexprs = lappend(propexprs, propexpr);
1158 : : }
1159 : : }
1160 : 616 : systable_endscan(scan2);
1161 : 616 : table_close(rel2, AccessShareLock);
1162 : : }
1163 : :
1164 : 472 : systable_endscan(scan1);
1165 : 472 : table_close(rel1, AccessShareLock);
1166 : 472 : }
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 : 952 : check_element_label_properties(Oid ellabeloid)
1183 : : {
1184 : : Relation rel;
1185 : : SysScanDesc scan;
1186 : : ScanKeyData key[1];
1187 : : HeapTuple tuple;
1188 : 952 : Oid labelid = InvalidOid;
1189 : 952 : Oid ref_ellabeloid = InvalidOid;
1190 : : List *myprops,
1191 : : *refprops;
1192 : : List *diff1,
1193 : : *diff2;
1194 : :
1195 : 952 : rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1196 : :
1197 : : /*
1198 : : * Get element label info
1199 : : */
1200 : 952 : ScanKeyInit(&key[0],
1201 : : Anum_pg_propgraph_element_label_oid,
1202 : : BTEqualStrategyNumber,
1203 : : F_OIDEQ, ObjectIdGetDatum(ellabeloid));
1204 : 952 : scan = systable_beginscan(rel, PropgraphElementLabelObjectIndexId, true, NULL, 1, key);
1205 [ + - ]: 952 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1206 : : {
1207 : 952 : Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
1208 : :
1209 : 952 : labelid = ellabel->pgellabelid;
1210 : : }
1211 : 952 : systable_endscan(scan);
1212 [ - + ]: 952 : if (!labelid)
50 peter@eisentraut.org 1213 [ # # ]:UNC 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 same label OID as the one being
1218 : : * checked but a different element OID.
1219 : : */
50 peter@eisentraut.org 1220 :GNC 952 : ScanKeyInit(&key[0],
1221 : : Anum_pg_propgraph_element_label_pgellabelid,
1222 : : BTEqualStrategyNumber,
1223 : : F_OIDEQ, ObjectIdGetDatum(labelid));
1224 : 952 : scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
1225 [ + + ]: 1579 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1226 : : {
1227 : 1081 : Form_pg_propgraph_element_label otherellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
1228 : :
1229 [ + + ]: 1081 : if (otherellabel->oid != ellabeloid)
1230 : : {
1231 : 454 : ref_ellabeloid = otherellabel->oid;
1232 : 454 : break;
1233 : : }
1234 : : }
1235 : 952 : systable_endscan(scan);
1236 : :
1237 : 952 : table_close(rel, AccessShareLock);
1238 : :
1239 : : /*
1240 : : * If there is no previous definition of this label, then we are done.
1241 : : */
1242 [ + + ]: 952 : if (!ref_ellabeloid)
1243 : 498 : 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 : 188 : check_all_labels_properties(Oid pgrelid)
1277 : : {
1278 [ + + + + : 955 : foreach_oid(labeloid, get_graph_label_ids(pgrelid))
+ + ]
1279 : : {
1280 [ + - + + : 2094 : foreach_oid(ellabeloid, get_label_element_label_ids(labeloid))
+ + ]
1281 : : {
1282 : 912 : check_element_label_properties(ellabeloid);
1283 : : }
1284 : : }
1285 : 176 : }
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 : : {
50 peter@eisentraut.org 1304 [ # # ]:UNC 0 : ereport(NOTICE,
1305 : : (errmsg("relation \"%s\" does not exist, skipping",
1306 : : stmt->pgname->relname)));
1307 : 0 : return InvalidObjectAddress;
1308 : : }
1309 : :
50 peter@eisentraut.org 1310 :GNC 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)
50 peter@eisentraut.org 1377 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1383 [ - + ]:GNC 36 : if (edge->etable->alias)
50 peter@eisentraut.org 1384 :UNC 0 : einfo->aliasname = edge->etable->alias->aliasname;
1385 : : else
50 peter@eisentraut.org 1386 :GNC 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)))
50 peter@eisentraut.org 1416 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1422 :GNC 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 [ - + ]: 28 : 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
50 peter@eisentraut.org 1478 :UNC 0 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1479 : :
50 peter@eisentraut.org 1480 :GNC 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
50 peter@eisentraut.org 1500 :UNC 0 : peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
1501 : :
50 peter@eisentraut.org 1502 :GNC 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)
50 peter@eisentraut.org 1519 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1525 :GNC 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)
50 peter@eisentraut.org 1553 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1559 :GNC 8 : ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
1560 : : Anum_pg_propgraph_element_label_oid,
1561 : : ObjectIdGetDatum(peoid),
1562 : : ObjectIdGetDatum(labeloid));
1563 [ - + ]: 8 : if (!ellabeloid)
50 peter@eisentraut.org 1564 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1570 :GNC 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)
50 peter@eisentraut.org 1596 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1602 :GNC 8 : ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL,
1603 : : Anum_pg_propgraph_element_label_oid,
1604 : : ObjectIdGetDatum(peoid),
1605 : : ObjectIdGetDatum(labeloid));
1606 : :
1607 [ - + ]: 8 : if (!ellabeloid)
50 peter@eisentraut.org 1608 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1614 [ + - + + :GNC 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)
50 peter@eisentraut.org 1625 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1631 :GNC 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 property 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)
50 peter@eisentraut.org 1691 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1697 [ - + ]:GNC 128 : if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_VERTEX)
50 peter@eisentraut.org 1698 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1704 :GNC 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)
50 peter@eisentraut.org 1723 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1729 [ - + ]:GNC 20 : if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_EDGE)
50 peter@eisentraut.org 1730 [ # # ]:UNC 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 : :
50 peter@eisentraut.org 1736 :GNC 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)
50 peter@eisentraut.org 1754 [ # # ]:UNC 0 : elog(ERROR, "cache lookup failed for property graph element %u", peid);
1755 : :
50 peter@eisentraut.org 1756 :GNC 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 : 200 : get_graph_label_ids(Oid graphid)
1768 : : {
1769 : : Relation rel;
1770 : : SysScanDesc scan;
1771 : : ScanKeyData key[1];
1772 : : HeapTuple tuple;
1773 : 200 : List *result = NIL;
1774 : :
1775 : 200 : rel = table_open(PropgraphLabelRelationId, AccessShareLock);
1776 : 200 : ScanKeyInit(&key[0],
1777 : : Anum_pg_propgraph_label_pglpgid,
1778 : : BTEqualStrategyNumber,
1779 : : F_OIDEQ, ObjectIdGetDatum(graphid));
1780 : 200 : scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId, true, NULL, 1, key);
1781 [ + + ]: 867 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1782 : : {
1783 : 667 : result = lappend_oid(result, ((Form_pg_propgraph_label) GETSTRUCT(tuple))->oid);
1784 : : }
1785 : 200 : systable_endscan(scan);
1786 : 200 : table_close(rel, AccessShareLock);
1787 : :
1788 : 200 : return result;
1789 : : }
1790 : :
1791 : : /*
1792 : : * Get a list of all element label OIDs for a label.
1793 : : */
1794 : : static List *
1795 : 675 : get_label_element_label_ids(Oid labelid)
1796 : : {
1797 : : Relation rel;
1798 : : SysScanDesc scan;
1799 : : ScanKeyData key[1];
1800 : : HeapTuple tuple;
1801 : 675 : List *result = NIL;
1802 : :
1803 : 675 : rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
1804 : 675 : ScanKeyInit(&key[0],
1805 : : Anum_pg_propgraph_element_label_pgellabelid,
1806 : : BTEqualStrategyNumber,
1807 : : F_OIDEQ, ObjectIdGetDatum(labelid));
1808 : 675 : scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key);
1809 [ + + ]: 1715 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1810 : : {
1811 : 1040 : result = lappend_oid(result, ((Form_pg_propgraph_element_label) GETSTRUCT(tuple))->oid);
1812 : : }
1813 : 675 : systable_endscan(scan);
1814 : 675 : table_close(rel, AccessShareLock);
1815 : :
1816 : 675 : 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 : : }
|