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