Coverage Report

Created: 2026-08-13 07:12

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