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